diff -Nru snapd-2.48+20.04/asserts/model.go snapd-2.48.3+20.04/asserts/model.go --- snapd-2.48+20.04/asserts/model.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/asserts/model.go 2021-02-02 08:21:12.000000000 +0000 @@ -339,6 +339,25 @@ ModelDangerous ModelGrade = "dangerous" ) +// StorageSafety characterizes the requested storage safety of +// the model which then controls what encryption is used +type StorageSafety string + +const ( + StorageSafetyUnset StorageSafety = "unset" + // StorageSafetyEncrypted implies mandatory full disk encryption. + StorageSafetyEncrypted StorageSafety = "encrypted" + // StorageSafetyPreferEncrypted implies full disk + // encryption when the system supports it. + StorageSafetyPreferEncrypted StorageSafety = "prefer-encrypted" + // StorageSafetyPreferUnencrypted implies no full disk + // encryption by default even if the system supports + // encryption. + StorageSafetyPreferUnencrypted StorageSafety = "prefer-unencrypted" +) + +var validStorageSafeties = []string{string(StorageSafetyEncrypted), string(StorageSafetyPreferEncrypted), string(StorageSafetyPreferUnencrypted)} + var validModelGrades = []string{string(ModelSecured), string(ModelSigned), string(ModelDangerous)} // gradeToCode encodes grades into 32 bits, trying to be slightly future-proof: @@ -374,6 +393,8 @@ grade ModelGrade + storageSafety StorageSafety + allSnaps []*ModelSnap // consumers of this info should care only about snap identity => // snapRef @@ -426,6 +447,12 @@ return mod.grade } +// StorageSafety returns the storage safety for the model. Will be +// StorageSafetyUnset for Core 16/18 models. +func (mod *Model) StorageSafety() StorageSafety { + return mod.storageSafety +} + // GadgetSnap returns the details of the gadget snap the model uses. func (mod *Model) GadgetSnap() *ModelSnap { return mod.gadgetSnap @@ -643,6 +670,9 @@ if _, ok := assert.headers["grade"]; ok { return nil, fmt.Errorf("cannot specify a grade for model without the extended snaps header") } + if _, ok := assert.headers["storage-safety"]; ok { + return nil, fmt.Errorf("cannot specify storage-safety for model without the extended snaps header") + } } if classic { @@ -694,6 +724,7 @@ var modSnaps *modelSnaps grade := ModelGradeUnset + storageSafety := StorageSafetyUnset if extended { gradeStr, err := checkOptionalString(assert.headers, "grade") if err != nil { @@ -707,6 +738,27 @@ grade = ModelGrade(gradeStr) } + storageSafetyStr, err := checkOptionalString(assert.headers, "storage-safety") + if err != nil { + return nil, err + } + if storageSafetyStr != "" && !strutil.ListContains(validStorageSafeties, storageSafetyStr) { + return nil, fmt.Errorf("storage-safety for model must be %s, not %q", strings.Join(validStorageSafeties, "|"), storageSafetyStr) + } + if storageSafetyStr != "" { + storageSafety = StorageSafety(storageSafetyStr) + } else { + if grade == ModelSecured { + storageSafety = StorageSafetyEncrypted + } else { + storageSafety = StorageSafetyPreferEncrypted + } + } + + if grade == ModelSecured && storageSafety != StorageSafetyEncrypted { + return nil, fmt.Errorf(`secured grade model must not have storage-safety overridden, only "encrypted" is valid`) + } + modSnaps, err = checkExtendedSnaps(extendedSnaps, base, grade) if err != nil { return nil, err @@ -794,6 +846,7 @@ gadgetSnap: modSnaps.gadget, kernelSnap: modSnaps.kernel, grade: grade, + storageSafety: storageSafety, allSnaps: allSnaps, requiredWithEssentialSnaps: requiredWithEssentialSnaps, numEssentialSnaps: numEssentialSnaps, diff -Nru snapd-2.48+20.04/asserts/model_test.go snapd-2.48.3+20.04/asserts/model_test.go --- snapd-2.48+20.04/asserts/model_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/asserts/model_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -133,6 +133,7 @@ type: app presence: optional OTHERgrade: secured +storage-safety: encrypted ` + "TSLINE" + "body-length: 0\n" + "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + @@ -178,6 +179,7 @@ }) c.Check(model.Store(), Equals, "brand-store") c.Check(model.Grade(), Equals, asserts.ModelGradeUnset) + c.Check(model.StorageSafety(), Equals, asserts.StorageSafetyUnset) essentialSnaps := model.EssentialSnaps() c.Check(essentialSnaps, DeepEquals, []*asserts.ModelSnap{ model.KernelSnap(), @@ -665,6 +667,7 @@ }) c.Check(model.Store(), Equals, "brand-store") c.Check(model.Grade(), Equals, asserts.ModelSecured) + c.Check(model.StorageSafety(), Equals, asserts.StorageSafetyEncrypted) essentialSnaps := model.EssentialSnaps() c.Check(essentialSnaps, DeepEquals, []*asserts.ModelSnap{ model.KernelSnap(), @@ -840,6 +843,55 @@ }) } +func (mods *modelSuite) TestCore20ValidStorageSafety(c *C) { + encoded := strings.Replace(core20ModelExample, "TSLINE", mods.tsLine, 1) + encoded = strings.Replace(encoded, "OTHER", "", 1) + encoded = strings.Replace(encoded, "grade: secured\n", "grade: signed\n", 1) + + for _, tc := range []struct { + ss asserts.StorageSafety + sss string + }{ + {asserts.StorageSafetyPreferEncrypted, "prefer-encrypted"}, + {asserts.StorageSafetyPreferUnencrypted, "prefer-unencrypted"}, + {asserts.StorageSafetyEncrypted, "encrypted"}, + } { + ex := strings.Replace(encoded, "storage-safety: encrypted\n", fmt.Sprintf("storage-safety: %s\n", tc.sss), 1) + a, err := asserts.Decode([]byte(ex)) + c.Assert(err, IsNil) + c.Check(a.Type(), Equals, asserts.ModelType) + model := a.(*asserts.Model) + c.Check(model.StorageSafety(), Equals, tc.ss) + } +} + +func (mods *modelSuite) TestCore20DefaultStorageSafetySecured(c *C) { + encoded := strings.Replace(core20ModelExample, "TSLINE", mods.tsLine, 1) + encoded = strings.Replace(encoded, "OTHER", "", 1) + ex := strings.Replace(encoded, "storage-safety: encrypted\n", "", 1) + + a, err := asserts.Decode([]byte(ex)) + c.Assert(err, IsNil) + c.Check(a.Type(), Equals, asserts.ModelType) + model := a.(*asserts.Model) + c.Check(model.StorageSafety(), Equals, asserts.StorageSafetyEncrypted) +} + +func (mods *modelSuite) TestCore20DefaultStorageSafetySignedDangerous(c *C) { + encoded := strings.Replace(core20ModelExample, "TSLINE", mods.tsLine, 1) + encoded = strings.Replace(encoded, "OTHER", "", 1) + encoded = strings.Replace(encoded, "storage-safety: encrypted\n", "", 1) + + for _, grade := range []string{"dangerous", "signed"} { + ex := strings.Replace(encoded, "grade: secured\n", fmt.Sprintf("grade: %s\n", grade), 1) + a, err := asserts.Decode([]byte(ex)) + c.Assert(err, IsNil) + c.Check(a.Type(), Equals, asserts.ModelType) + model := a.(*asserts.Model) + c.Check(model.StorageSafety(), Equals, asserts.StorageSafetyPreferEncrypted) + } +} + func (mods *modelSuite) TestCore20DecodeInvalid(c *C) { encoded := strings.Replace(core20ModelExample, "TSLINE", mods.tsLine, 1) @@ -878,6 +930,8 @@ {"OTHER", "gadget: foo\n", `cannot specify separate "gadget" header once using the extended snaps header`}, {"OTHER", "required-snaps:\n - foo\n", `cannot specify separate "required-snaps" header once using the extended snaps header`}, {"grade: secured\n", "grade: foo\n", `grade for model must be secured|signed|dangerous`}, + {"storage-safety: encrypted\n", "storage-safety: foo\n", `storage-safety for model must be encrypted\|prefer-encrypted\|prefer-unencrypted, not "foo"`}, + {"storage-safety: encrypted\n", "storage-safety: prefer-unencrypted\n", `secured grade model must not have storage-safety overridden, only "encrypted" is valid`}, } for _, test := range invalidTests { invalid := strings.Replace(encoded, test.original, test.invalid, 1) diff -Nru snapd-2.48+20.04/boot/assets.go snapd-2.48.3+20.04/boot/assets.go --- snapd-2.48+20.04/boot/assets.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/boot/assets.go 2021-02-02 08:21:12.000000000 +0000 @@ -355,7 +355,17 @@ } // trusted assets need tracking only when the system is using encryption // for its data partitions - trackTrustedAssets := hasSealedKeys(dirs.GlobalRootDir) + trackTrustedAssets := false + _, err := sealedKeysMethod(dirs.GlobalRootDir) + switch { + case err == nil: + trackTrustedAssets = true + case err == errNoSealedKeys: + // nothing to do + case err != nil: + // all other errors + return nil, err + } // see what we need to observe for the run bootloader runBl, runTrusted, runManaged, err := gadgetMaybeTrustedBootloaderAndAssets(gadgetDir, InitramfsUbuntuBootDir, diff -Nru snapd-2.48+20.04/boot/export_test.go snapd-2.48.3+20.04/boot/export_test.go --- snapd-2.48+20.04/boot/export_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/boot/export_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -166,3 +166,25 @@ func (b *bootChain) KernelBootFile() bootloader.BootFile { return b.kernelBootFile } + +func MockHasFDESetupHook(f func() (bool, error)) (restore func()) { + oldHasFDESetupHook := HasFDESetupHook + HasFDESetupHook = f + return func() { + HasFDESetupHook = oldHasFDESetupHook + } +} + +func MockRunFDESetupHook(f func(string, *FDESetupHookParams) ([]byte, error)) (restore func()) { + oldRunFDESetupHook := RunFDESetupHook + RunFDESetupHook = f + return func() { RunFDESetupHook = oldRunFDESetupHook } +} + +func MockResealKeyToModeenvUsingFDESetupHook(f func(string, *asserts.Model, *Modeenv, bool) error) (restore func()) { + old := resealKeyToModeenvUsingFDESetupHook + resealKeyToModeenvUsingFDESetupHook = f + return func() { + resealKeyToModeenvUsingFDESetupHook = old + } +} diff -Nru snapd-2.48+20.04/boot/seal.go snapd-2.48.3+20.04/boot/seal.go --- snapd-2.48+20.04/boot/seal.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/boot/seal.go 2021-02-02 08:21:12.000000000 +0000 @@ -24,7 +24,9 @@ "crypto/elliptic" "crypto/rand" "encoding/json" + "errors" "fmt" + "io/ioutil" "os" "path/filepath" @@ -48,23 +50,31 @@ ) // Hook functions setup by devicestate to support device-specific full -// disk encryption implementations. +// disk encryption implementations. The state must be locked when these +// functions are called. var ( HasFDESetupHook = func() (bool, error) { return false, nil } - RunFDESetupHook = func(op string, params *FdeSetupHookParams) error { - return fmt.Errorf("internal error: RunFDESetupHook not set yet") + RunFDESetupHook = func(op string, params *FDESetupHookParams) ([]byte, error) { + return nil, fmt.Errorf("internal error: RunFDESetupHook not set yet") } ) -// FdeSetupHookParams contains the inputs for the fde-setup hook -type FdeSetupHookParams struct { +type sealingMethod string + +const ( + sealingMethodLegacyTPM = sealingMethod("") + sealingMethodTPM = sealingMethod("tpm") + sealingMethodFDESetupHook = sealingMethod("fde-setup-hook") +) + +// FDESetupHookParams contains the inputs for the fde-setup hook +type FDESetupHookParams struct { Key secboot.EncryptionKey KeyName string - KernelInfo *snap.Info - Model *asserts.Model + Models []*asserts.Model //TODO:UC20: provide bootchains and a way to track measured //boot-assets @@ -82,19 +92,76 @@ // in modeenv. // It assumes to be invoked in install mode. func sealKeyToModeenv(key, saveKey secboot.EncryptionKey, model *asserts.Model, modeenv *Modeenv) error { + // make sure relevant locations exist + for _, p := range []string{ + InitramfsSeedEncryptionKeyDir, + InitramfsBootEncryptionKeyDir, + InstallHostFDEDataDir, + InstallHostFDESaveDir, + } { + // XXX: should that be 0700 ? + if err := os.MkdirAll(p, 0755); err != nil { + return err + } + } + hasHook, err := HasFDESetupHook() if err != nil { return fmt.Errorf("cannot check for fde-setup hook %v", err) } if hasHook { - return sealKeyToModeenvUsingFdeSetupHook(key, saveKey, model, modeenv) + return sealKeyToModeenvUsingFDESetupHook(key, saveKey, model, modeenv) } return sealKeyToModeenvUsingSecboot(key, saveKey, model, modeenv) } -func sealKeyToModeenvUsingFdeSetupHook(key, saveKey secboot.EncryptionKey, model *asserts.Model, modeenv *Modeenv) error { - return fmt.Errorf("cannot use fde-setup hook yet") +func runKeySealRequests(key secboot.EncryptionKey) []secboot.SealKeyRequest { + return []secboot.SealKeyRequest{ + { + Key: key, + KeyFile: filepath.Join(InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), + }, + } +} + +func fallbackKeySealRequests(key, saveKey secboot.EncryptionKey) []secboot.SealKeyRequest { + return []secboot.SealKeyRequest{ + { + Key: key, + KeyFile: filepath.Join(InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), + }, + { + Key: saveKey, + KeyFile: filepath.Join(InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), + }, + } +} + +func sealKeyToModeenvUsingFDESetupHook(key, saveKey secboot.EncryptionKey, model *asserts.Model, modeenv *Modeenv) error { + // TODO: support full boot chains + + for _, skr := range append(runKeySealRequests(key), fallbackKeySealRequests(key, saveKey)...) { + params := &FDESetupHookParams{ + Key: skr.Key, + // TODO: decide what the right KeyName is + // KeyName: filepath.Base(skr.KeyFile), + Models: []*asserts.Model{model}, + } + sealedKey, err := RunFDESetupHook("initial-setup", params) + if err != nil { + return err + } + if err := osutil.AtomicWriteFile(filepath.Join(skr.KeyFile), sealedKey, 0600, 0); err != nil { + return fmt.Errorf("cannot store key: %v", err) + } + } + + if err := stampSealedKeys(InstallHostWritableDir, "fde-setup-hook"); err != nil { + return err + } + + return nil } func sealKeyToModeenvUsingSecboot(key, saveKey secboot.EncryptionKey, model *asserts.Model, modeenv *Modeenv) error { @@ -141,19 +208,6 @@ bootloader.RoleRunMode: bl.Name(), } - // make sure relevant locations exist - for _, p := range []string{ - InitramfsSeedEncryptionKeyDir, - InitramfsBootEncryptionKeyDir, - InstallHostFDEDataDir, - InstallHostFDESaveDir, - } { - // XXX: should that be 0700 ? - if err := os.MkdirAll(p, 0755); err != nil { - return err - } - } - // the boot chains we seal the fallback object to rpbc := toPredictableBootChains(recoveryBootChains) @@ -171,7 +225,7 @@ return err } - if err := stampSealedKeys(InstallHostWritableDir); err != nil { + if err := stampSealedKeys(InstallHostWritableDir, sealingMethodTPM); err != nil { return err } @@ -207,13 +261,7 @@ // path only unseals one object because unsealing is expensive. // Furthermore, the run object key is stored on ubuntu-boot so that we do not // need to continually write/read keys from ubuntu-seed. - keys := []secboot.SealKeyRequest{ - { - Key: key, - KeyFile: filepath.Join(InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), - }, - } - if err := secbootSealKeys(keys, sealKeyParams); err != nil { + if err := secbootSealKeys(runKeySealRequests(key), sealKeyParams); err != nil { return fmt.Errorf("cannot seal the encryption keys: %v", err) } @@ -234,51 +282,80 @@ // The fallback object contains the ubuntu-data and ubuntu-save keys. The // key files are stored on ubuntu-seed, separate from ubuntu-data so they // can be used if ubuntu-data and ubuntu-boot are corrupted or unavailable. - keys := []secboot.SealKeyRequest{ - { - Key: key, - KeyFile: filepath.Join(InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), - }, - { - Key: saveKey, - KeyFile: filepath.Join(InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), - }, - } - if err := secbootSealKeys(keys, sealKeyParams); err != nil { + if err := secbootSealKeys(fallbackKeySealRequests(key, saveKey), sealKeyParams); err != nil { return fmt.Errorf("cannot seal the fallback encryption keys: %v", err) } return nil } -func stampSealedKeys(rootdir string) error { +func stampSealedKeys(rootdir string, content sealingMethod) error { stamp := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys") if err := os.MkdirAll(filepath.Dir(stamp), 0755); err != nil { return fmt.Errorf("cannot create device fde state directory: %v", err) } - if err := osutil.AtomicWriteFile(stamp, nil, 0644, 0); err != nil { + if err := osutil.AtomicWriteFile(stamp, []byte(content), 0644, 0); err != nil { return fmt.Errorf("cannot create fde sealed keys stamp file: %v", err) } return nil } -// hasSealedKeys return whether any keys were sealed at all -func hasSealedKeys(rootdir string) bool { +var errNoSealedKeys = errors.New("no sealed keys") + +// sealedKeysMethod return whether any keys were sealed at all +func sealedKeysMethod(rootdir string) (sm sealingMethod, err error) { // TODO:UC20: consider more than the marker for cases where we reseal // outside of run mode stamp := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys") - return osutil.FileExists(stamp) + content, err := ioutil.ReadFile(stamp) + if os.IsNotExist(err) { + return sm, errNoSealedKeys + } + return sealingMethod(content), err } // resealKeyToModeenv reseals the existing encryption key to the // parameters specified in modeenv. func resealKeyToModeenv(rootdir string, model *asserts.Model, modeenv *Modeenv, expectReseal bool) error { - if !hasSealedKeys(rootdir) { + method, err := sealedKeysMethod(rootdir) + if err == errNoSealedKeys { // nothing to do return nil } + if err != nil { + return err + } + switch method { + case sealingMethodFDESetupHook: + return resealKeyToModeenvUsingFDESetupHook(rootdir, model, modeenv, expectReseal) + case sealingMethodTPM, sealingMethodLegacyTPM: + return resealKeyToModeenvSecboot(rootdir, model, modeenv, expectReseal) + default: + return fmt.Errorf("unknown key sealing method: %q", method) + } +} + +var resealKeyToModeenvUsingFDESetupHook = resealKeyToModeenvUsingFDESetupHookImpl + +func resealKeyToModeenvUsingFDESetupHookImpl(rootdir string, model *asserts.Model, modeenv *Modeenv, expectReseal bool) error { + // TODO: Implement reseal using the fde-setup hook. This will + // require a helper like "FDEShouldResealUsingSetupHook" + // that will be set by devicestate and returns (bool, + // error). It needs to return "false" during seeding + // because then there is no kernel available yet. It + // can though return true as soon as there's an active + // kernel if seeded is false + // + // It will also need to run HasFDESetupHook internally + // and return an error if the hook goes missing + // (e.g. because a kernel refresh losses the hook by + // accident). It could also run features directly and + // check for "reseal" in features. + return nil +} +func resealKeyToModeenvSecboot(rootdir string, model *asserts.Model, modeenv *Modeenv, expectReseal bool) error { // build the recovery mode boot chain rbl, err := bootloader.Find(InitramfsUbuntuSeedDir, &bootloader.Options{ Role: bootloader.RoleRecovery, diff -Nru snapd-2.48+20.04/boot/seal_test.go snapd-2.48.3+20.04/boot/seal_test.go --- snapd-2.48+20.04/boot/seal_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/boot/seal_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -25,6 +25,7 @@ "io/ioutil" "os" "path/filepath" + "strconv" . "gopkg.in/check.v1" @@ -293,8 +294,8 @@ }) // marker - c.Check(filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "sealed-keys"), testutil.FilePresent) - + marker := filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "sealed-keys") + c.Check(marker, testutil.FileEquals, "tpm") } } @@ -901,30 +902,143 @@ c.Check(cnt, Equals, 3) } -func (s *sealSuite) TestSealToModeenvWithFdeHookFailsToday(c *C) { +func (s *sealSuite) TestSealToModeenvWithFdeHookHappy(c *C) { rootdir := c.MkDir() dirs.SetRootDir(rootdir) defer dirs.SetRootDir("") - oldHasFDESetupHook := boot.HasFDESetupHook - defer func() { boot.HasFDESetupHook = oldHasFDESetupHook }() - boot.HasFDESetupHook = func() (bool, error) { + restore := boot.MockHasFDESetupHook(func() (bool, error) { return true, nil + }) + defer restore() + + n := 0 + var runFDESetupHookParams []*boot.FDESetupHookParams + restore = boot.MockRunFDESetupHook(func(op string, params *boot.FDESetupHookParams) ([]byte, error) { + n++ + c.Assert(op, Equals, "initial-setup") + runFDESetupHookParams = append(runFDESetupHookParams, params) + return []byte("sealed-key: " + strconv.Itoa(n)), nil + }) + defer restore() + + modeenv := &boot.Modeenv{ + RecoverySystem: "20200825", } - oldRunFDESetupHook := boot.RunFDESetupHook - defer func() { boot.RunFDESetupHook = oldRunFDESetupHook }() - boot.RunFDESetupHook = func(string, *boot.FdeSetupHookParams) error { - c.Fatalf("hook runner should not be called yet") + key := secboot.EncryptionKey{1, 2, 3, 4} + saveKey := secboot.EncryptionKey{5, 6, 7, 8} + + model := boottest.MakeMockUC20Model() + err := boot.SealKeyToModeenv(key, saveKey, model, modeenv) + c.Assert(err, IsNil) + // check that runFDESetupHook was called the expected way + c.Check(runFDESetupHookParams, DeepEquals, []*boot.FDESetupHookParams{ + {Key: secboot.EncryptionKey{1, 2, 3, 4}, Models: []*asserts.Model{model}}, + {Key: secboot.EncryptionKey{1, 2, 3, 4}, Models: []*asserts.Model{model}}, + {Key: secboot.EncryptionKey{5, 6, 7, 8}, Models: []*asserts.Model{model}}, + }) + // check that the sealed keys got written to the expected places + for i, p := range []string{ + filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), + filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), + filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), + } { + c.Check(p, testutil.FileEquals, "sealed-key: "+strconv.Itoa(i+1)) + } + marker := filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "sealed-keys") + c.Check(marker, testutil.FileEquals, "fde-setup-hook") +} + +func (s *sealSuite) TestSealToModeenvWithFdeHookSad(c *C) { + rootdir := c.MkDir() + dirs.SetRootDir(rootdir) + defer dirs.SetRootDir("") + + restore := boot.MockHasFDESetupHook(func() (bool, error) { + return true, nil + }) + defer restore() + + restore = boot.MockRunFDESetupHook(func(op string, params *boot.FDESetupHookParams) ([]byte, error) { + return nil, fmt.Errorf("hook failed") + }) + defer restore() + + modeenv := &boot.Modeenv{ + RecoverySystem: "20200825", + } + key := secboot.EncryptionKey{1, 2, 3, 4} + saveKey := secboot.EncryptionKey{5, 6, 7, 8} + + model := boottest.MakeMockUC20Model() + err := boot.SealKeyToModeenv(key, saveKey, model, modeenv) + c.Assert(err, ErrorMatches, "hook failed") + marker := filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "sealed-keys") + c.Check(marker, testutil.FileAbsent) +} + +func (s *sealSuite) TestResealKeyToModeenvWithFdeHookCalled(c *C) { + rootdir := c.MkDir() + dirs.SetRootDir(rootdir) + defer dirs.SetRootDir("") + + resealKeyToModeenvUsingFDESetupHookCalled := 0 + restore := boot.MockResealKeyToModeenvUsingFDESetupHook(func(string, *asserts.Model, *boot.Modeenv, bool) error { + resealKeyToModeenvUsingFDESetupHookCalled++ return nil + }) + defer restore() + + // TODO: this simulates that the hook is not available yet + // because of e.g. seeding. Longer term there will be + // more, see TODO in resealKeyToModeenvUsingFDESetupHookImpl + restore = boot.MockHasFDESetupHook(func() (bool, error) { + return false, fmt.Errorf("hook not available yet because e.g. seeding") + }) + defer restore() + + marker := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys") + err := os.MkdirAll(filepath.Dir(marker), 0755) + c.Assert(err, IsNil) + err = ioutil.WriteFile(marker, []byte("fde-setup-hook"), 0644) + c.Assert(err, IsNil) + + modeenv := &boot.Modeenv{ + RecoverySystem: "20200825", } + model := boottest.MakeMockUC20Model() + expectReseal := false + err = boot.ResealKeyToModeenv(rootdir, model, modeenv, expectReseal) + c.Assert(err, IsNil) + c.Check(resealKeyToModeenvUsingFDESetupHookCalled, Equals, 1) +} + +func (s *sealSuite) TestResealKeyToModeenvWithFdeHookVerySad(c *C) { + rootdir := c.MkDir() + dirs.SetRootDir(rootdir) + defer dirs.SetRootDir("") + + resealKeyToModeenvUsingFDESetupHookCalled := 0 + restore := boot.MockResealKeyToModeenvUsingFDESetupHook(func(string, *asserts.Model, *boot.Modeenv, bool) error { + resealKeyToModeenvUsingFDESetupHookCalled++ + return fmt.Errorf("fde setup hook failed") + }) + defer restore() + + marker := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys") + err := os.MkdirAll(filepath.Dir(marker), 0755) + c.Assert(err, IsNil) + err = ioutil.WriteFile(marker, []byte("fde-setup-hook"), 0644) + c.Assert(err, IsNil) + modeenv := &boot.Modeenv{ RecoverySystem: "20200825", } - myKey := secboot.EncryptionKey{} - myKey2 := secboot.EncryptionKey{} model := boottest.MakeMockUC20Model() - err := boot.SealKeyToModeenv(myKey, myKey2, model, modeenv) - c.Assert(err, ErrorMatches, "cannot use fde-setup hook yet") + expectReseal := false + err = boot.ResealKeyToModeenv(rootdir, model, modeenv, expectReseal) + c.Assert(err, ErrorMatches, "fde setup hook failed") + c.Check(resealKeyToModeenvUsingFDESetupHookCalled, Equals, 1) } diff -Nru snapd-2.48+20.04/client/client_test.go snapd-2.48.3+20.04/client/client_test.go --- snapd-2.48+20.04/client/client_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/client/client_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -435,7 +435,7 @@ Args: []string{"bar", "--baz"}, } - stdout, stderr, err := cli.RunSnapctl(options) + stdout, stderr, err := cli.RunSnapctl(options, nil) c.Check(err, IsNil) c.Check(string(stdout), Equals, "test stdout") c.Check(string(stderr), Equals, "test stderr") diff -Nru snapd-2.48+20.04/client/snapctl.go snapd-2.48.3+20.04/client/snapctl.go --- snapd-2.48+20.04/client/snapctl.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/client/snapctl.go 2021-02-02 08:21:12.000000000 +0000 @@ -23,8 +23,21 @@ "bytes" "encoding/json" "fmt" + "io" + "io/ioutil" ) +// InternalSsnapctlCmdNeedsStdin returns true if the given snapctl command +// needs data from stdin +func InternalSnapctlCmdNeedsStdin(name string) bool { + switch name { + case "fde-setup-result": + return true + default: + return false + } +} + // SnapCtlOptions holds the various options with which snapctl is invoked. type SnapCtlOptions struct { // ContextID is a string used to determine the context of this call (e.g. @@ -35,14 +48,36 @@ Args []string `json:"args"` } +// SnapCtlPostData is the data posted to the daemon /v2/snapctl endpoint +// TODO: this can be removed again once we no longer need to pass stdin data +// but instead use a real stdin stream +type SnapCtlPostData struct { + SnapCtlOptions + + Stdin []byte `json:"stdin,omitempty"` +} + type snapctlOutput struct { Stdout string `json:"stdout"` Stderr string `json:"stderr"` } // RunSnapctl requests a snapctl run for the given options. -func (client *Client) RunSnapctl(options *SnapCtlOptions) (stdout, stderr []byte, err error) { - b, err := json.Marshal(options) +func (client *Client) RunSnapctl(options *SnapCtlOptions, stdin io.Reader) (stdout, stderr []byte, err error) { + // TODO: instead of reading all of stdin here we need to forward it to + // the daemon eventually + var stdinData []byte + if stdin != nil { + stdinData, err = ioutil.ReadAll(stdin) + if err != nil { + return nil, nil, fmt.Errorf("cannot read stdin: %v", err) + } + } + + b, err := json.Marshal(SnapCtlPostData{ + SnapCtlOptions: *options, + Stdin: stdinData, + }) if err != nil { return nil, nil, fmt.Errorf("cannot marshal options: %s", err) } diff -Nru snapd-2.48+20.04/client/snapctl_test.go snapd-2.48.3+20.04/client/snapctl_test.go --- snapd-2.48+20.04/client/snapctl_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/client/snapctl_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -20,6 +20,8 @@ package client_test import ( + "bytes" + "encoding/base64" "encoding/json" "github.com/snapcore/snapd/client" @@ -32,7 +34,7 @@ ContextID: "1234ABCD", Args: []string{"foo", "bar"}, } - cs.cli.RunSnapctl(options) + cs.cli.RunSnapctl(options, nil) c.Check(cs.req.Method, check.Equals, "POST") c.Check(cs.req.URL.Path, check.Equals, "/v2/snapctl") } @@ -47,12 +49,13 @@ } }` + mockStdin := bytes.NewBufferString("some-input") options := &client.SnapCtlOptions{ ContextID: "1234ABCD", Args: []string{"foo", "bar"}, } - stdout, stderr, err := cs.cli.RunSnapctl(options) + stdout, stderr, err := cs.cli.RunSnapctl(options, mockStdin) c.Assert(err, check.IsNil) c.Check(string(stdout), check.Equals, "test stdout") c.Check(string(stderr), check.Equals, "test stderr") @@ -64,5 +67,18 @@ c.Check(body, check.DeepEquals, map[string]interface{}{ "context-id": "1234ABCD", "args": []interface{}{"foo", "bar"}, + + // json byte-stream is b64 encoded + "stdin": base64.StdEncoding.EncodeToString([]byte("some-input")), }) } + +func (cs *clientSuite) TestInternalSnapctlCmdNeedsStdin(c *check.C) { + res := client.InternalSnapctlCmdNeedsStdin("fde-setup-result") + c.Check(res, check.Equals, true) + + for _, s := range []string{"help", "other"} { + res := client.InternalSnapctlCmdNeedsStdin(s) + c.Check(res, check.Equals, false) + } +} diff -Nru snapd-2.48+20.04/cmd/snap/cmd_run.go snapd-2.48.3+20.04/cmd/snap/cmd_run.go --- snapd-2.48+20.04/cmd/snap/cmd_run.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap/cmd_run.go 2021-02-02 08:21:12.000000000 +0000 @@ -1014,6 +1014,22 @@ } if info.Base != "" { cmd = append(cmd, "--base", info.Base) + } else { + if info.Type() == snap.TypeKernel { + // kernels have no explicit base, we use the boot base + modelAssertion, err := x.client.CurrentModelAssertion() + if err != nil { + if hook != "" { + return fmt.Errorf("cannot get model assertion to setup kernel hook run: %v", err) + } else { + return fmt.Errorf("cannot get model assertion to setup kernel app run: %v", err) + } + } + modelBase := modelAssertion.Base() + if modelBase != "" { + cmd = append(cmd, "--base", modelBase) + } + } } cmd = append(cmd, securityTag) diff -Nru snapd-2.48+20.04/cmd/snap/cmd_run_test.go snapd-2.48.3+20.04/cmd/snap/cmd_run_test.go --- snapd-2.48+20.04/cmd/snap/cmd_run_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap/cmd_run_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -22,6 +22,7 @@ import ( "errors" "fmt" + "net/http" "os" "os/user" "path/filepath" @@ -1511,3 +1512,60 @@ // Ensure that the debug message is printed. c.Assert(logbuf.String(), testutil.Contains, "snapd cannot track the started application\n") } + +var mockKernelYaml = []byte(`name: pc-kernel +type: kernel +version: 1.0 +hooks: + fde-setup: +`) + +func (s *RunSuite) TestSnapRunHookKernelImplicitBase(c *check.C) { + defer mockSnapConfine(dirs.DistroLibExecDir)() + + nModel := 0 + s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/v2/model": + switch nModel { + case 0: + c.Check(r.Method, check.Equals, "GET") + c.Check(r.URL.RawQuery, check.Equals, "") + fmt.Fprintln(w, happyUC20ModelAssertionResponse) + default: + c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModel+1) + } + nModel++ + } + }) + + // mock installed kernel + snaptest.MockSnapCurrent(c, string(mockKernelYaml), &snap.SideInfo{ + Revision: snap.R(42), + }) + + // redirect exec + execArg0 := "" + execArgs := []string{} + execEnv := []string{} + restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { + execArg0 = arg0 + execArgs = args + execEnv = envv + return nil + }) + defer restorer() + + // Run a hook from the active revision + _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=fde-setup", "--", "pc-kernel"}) + c.Assert(err, check.IsNil) + c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) + c.Check(execArgs, check.DeepEquals, []string{ + filepath.Join(dirs.DistroLibExecDir, "snap-confine"), + "--base", "core20", + "snap.pc-kernel.hook.fde-setup", + filepath.Join(dirs.CoreLibExecDir, "snap-exec"), + "--hook=fde-setup", "pc-kernel"}) + c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") + c.Check(nModel, check.Equals, 1) +} diff -Nru snapd-2.48+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts.go snapd-2.48.3+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts.go --- snapd-2.48+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts.go 2021-02-02 08:21:12.000000000 +0000 @@ -82,7 +82,7 @@ secbootUnlockVolumeUsingSealedKeyIfEncrypted func(disk disks.Disk, name string, encryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) secbootUnlockEncryptedVolumeUsingKey func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) - secbootLockTPMSealedKeys func() error + secbootLockSealedKeys func() error bootFindPartitionUUIDForBootedKernelDisk = boot.FindPartitionUUIDForBootedKernelDisk ) @@ -105,7 +105,7 @@ // ensure that the last thing we do is to lock access to sealed keys, // regardless of mode or early failures. defer func() { - if e := secbootLockTPMSealedKeys(); e != nil { + if e := secbootLockSealedKeys(); e != nil { e = fmt.Errorf("error locking access to sealed keys: %v", e) if err == nil { err = e @@ -738,7 +738,7 @@ // check soundness // the grade check makes sure that if data was mounted unencrypted // but the model is secured it will end up marked as untrusted - isEncrypted := m.isEncryptedDev || m.model.Grade() == asserts.ModelSecured + isEncrypted := m.isEncryptedDev || m.model.StorageSafety() == asserts.StorageSafetyEncrypted part := m.degradedState.partition("ubuntu-data") if part.MountState == partitionMounted && isEncrypted { // check that save and data match diff -Nru snapd-2.48+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_nosecboot.go snapd-2.48.3+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_nosecboot.go --- snapd-2.48+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_nosecboot.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_nosecboot.go 2021-02-02 08:21:12.000000000 +0000 @@ -46,7 +46,7 @@ return secboot.UnlockResult{}, errNotImplemented } - secbootLockTPMSealedKeys = func() error { + secbootLockSealedKeys = func() error { return errNotImplemented } } diff -Nru snapd-2.48+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_secboot.go snapd-2.48.3+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_secboot.go --- snapd-2.48+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_secboot.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_secboot.go 2021-02-02 08:21:12.000000000 +0000 @@ -29,5 +29,5 @@ secbootMeasureSnapModelWhenPossible = secboot.MeasureSnapModelWhenPossible secbootUnlockVolumeUsingSealedKeyIfEncrypted = secboot.UnlockVolumeUsingSealedKeyIfEncrypted secbootUnlockEncryptedVolumeUsingKey = secboot.UnlockEncryptedVolumeUsingKey - secbootLockTPMSealedKeys = secboot.LockTPMSealedKeys + secbootLockSealedKeys = secboot.LockSealedKeys } diff -Nru snapd-2.48+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_test.go snapd-2.48.3+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_test.go --- snapd-2.48+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-bootstrap/cmd_initramfs_mounts_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -219,7 +219,7 @@ s.AddCleanup(main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) { return foundUnencrypted(name), nil })) - s.AddCleanup(main.MockSecbootLockTPMSealedKeys(func() error { + s.AddCleanup(main.MockSecbootLockSealedKeys(func() error { return nil })) } @@ -428,7 +428,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - defer main.MockSecbootLockTPMSealedKeys(func() error { + defer main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil })() @@ -648,7 +648,7 @@ func (s *initramfsMountsSuite) TestInitramfsMountsRunModeNoSaveUnencryptedHappy(c *C) { // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - defer main.MockSecbootLockTPMSealedKeys(func() error { + defer main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil })() @@ -661,7 +661,7 @@ func (s *initramfsMountsSuite) TestInitramfsMountsRunModeNoSaveUnencryptedKeyLockingUnhappy(c *C) { // have blocking sealed keys fail - defer main.MockSecbootLockTPMSealedKeys(func() error { + defer main.MockSecbootLockSealedKeys(func() error { return fmt.Errorf("blocking keys failed") })() @@ -1554,7 +1554,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - defer main.MockSecbootLockTPMSealedKeys(func() error { + defer main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil })() @@ -1760,7 +1760,7 @@ func (s *initramfsMountsSuite) TestInitramfsMountsRunModeEncryptedDataUnhappyUnlockSaveFail(c *C) { // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - defer main.MockSecbootLockTPMSealedKeys(func() error { + defer main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return fmt.Errorf("blocking keys failed") })() @@ -1853,7 +1853,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - defer main.MockSecbootLockTPMSealedKeys(func() error { + defer main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return fmt.Errorf("blocking keys failed") })() @@ -2246,7 +2246,7 @@ func (s *initramfsMountsSuite) testRecoverModeHappy(c *C) { // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - restore := main.MockSecbootLockTPMSealedKeys(func() error { + restore := main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil }) @@ -3466,7 +3466,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - restore = main.MockSecbootLockTPMSealedKeys(func() error { + restore = main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil }) @@ -3663,7 +3663,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - restore = main.MockSecbootLockTPMSealedKeys(func() error { + restore = main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil }) @@ -3859,7 +3859,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - restore = main.MockSecbootLockTPMSealedKeys(func() error { + restore = main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil }) @@ -4010,7 +4010,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - restore = main.MockSecbootLockTPMSealedKeys(func() error { + restore = main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil }) @@ -4204,7 +4204,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - restore = main.MockSecbootLockTPMSealedKeys(func() error { + restore = main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil }) @@ -4375,7 +4375,7 @@ // ensure that we check that access to sealed keys were locked sealedKeysLocked := false - restore = main.MockSecbootLockTPMSealedKeys(func() error { + restore = main.MockSecbootLockSealedKeys(func() error { sealedKeysLocked = true return nil }) diff -Nru snapd-2.48+20.04/cmd/snap-bootstrap/export_test.go snapd-2.48.3+20.04/cmd/snap-bootstrap/export_test.go --- snapd-2.48+20.04/cmd/snap-bootstrap/export_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-bootstrap/export_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -122,11 +122,11 @@ } } -func MockSecbootLockTPMSealedKeys(f func() error) (restore func()) { - old := secbootLockTPMSealedKeys - secbootLockTPMSealedKeys = f +func MockSecbootLockSealedKeys(f func() error) (restore func()) { + old := secbootLockSealedKeys + secbootLockSealedKeys = f return func() { - secbootLockTPMSealedKeys = old + secbootLockSealedKeys = old } } diff -Nru snapd-2.48+20.04/cmd/snap-bootstrap/main.go snapd-2.48.3+20.04/cmd/snap-bootstrap/main.go --- snapd-2.48+20.04/cmd/snap-bootstrap/main.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-bootstrap/main.go 2021-02-02 08:21:12.000000000 +0000 @@ -26,6 +26,9 @@ "github.com/jessevdk/go-flags" "github.com/snapcore/snapd/logger" + + // this import will init "secboot.FDEHasRevealKey" correctly + _ "github.com/snapcore/snapd/overlord/devicestate/fde" ) var ( diff -Nru snapd-2.48+20.04/cmd/snapctl/main.go snapd-2.48.3+20.04/cmd/snapctl/main.go --- snapd-2.48+20.04/cmd/snapctl/main.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snapctl/main.go 2021-02-02 08:21:12.000000000 +0000 @@ -21,6 +21,7 @@ import ( "fmt" + "io" "os" "github.com/snapcore/snapd/client" @@ -55,8 +56,13 @@ os.Exit(0) } + var stdin io.Reader + if len(os.Args) > 1 && client.InternalSnapctlCmdNeedsStdin(os.Args[1]) { + stdin = os.Stdin + } + // no internal command, route via snapd - stdout, stderr, err := run() + stdout, stderr, err := run(stdin) if err != nil { if e, ok := err.(*client.Error); ok { switch e.Kind { @@ -87,7 +93,7 @@ } } -func run() (stdout, stderr []byte, err error) { +func run(stdin io.Reader) (stdout, stderr []byte, err error) { cli := client.New(&clientConfig) cookie := os.Getenv("SNAP_COOKIE") @@ -98,5 +104,5 @@ return cli.RunSnapctl(&client.SnapCtlOptions{ ContextID: cookie, Args: os.Args[1:], - }) + }, stdin) } diff -Nru snapd-2.48+20.04/cmd/snapctl/main_test.go snapd-2.48.3+20.04/cmd/snapctl/main_test.go --- snapd-2.48+20.04/cmd/snapctl/main_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snapctl/main_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -20,6 +20,7 @@ package main import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -41,6 +42,7 @@ oldArgs []string expectedContextID string expectedArgs []string + expectedStdin []byte } var _ = Suite(&snapctlSuite{}) @@ -57,11 +59,12 @@ c.Assert(r.URL.Path, Equals, "/v2/snapctl") c.Assert(r.Header.Get("Authorization"), Equals, "") - var snapctlOptions client.SnapCtlOptions + var snapctlPostData client.SnapCtlPostData decoder := json.NewDecoder(r.Body) - c.Assert(decoder.Decode(&snapctlOptions), IsNil) - c.Assert(snapctlOptions.ContextID, Equals, s.expectedContextID) - c.Assert(snapctlOptions.Args, DeepEquals, s.expectedArgs) + c.Assert(decoder.Decode(&snapctlPostData), IsNil) + c.Assert(snapctlPostData.ContextID, Equals, s.expectedContextID) + c.Assert(snapctlPostData.Args, DeepEquals, s.expectedArgs) + c.Assert(snapctlPostData.Stdin, DeepEquals, s.expectedStdin) fmt.Fprintln(w, `{"type": "sync", "result": {"stdout": "test stdout", "stderr": "test stderr"}}`) default: @@ -91,7 +94,7 @@ } func (s *snapctlSuite) TestSnapctl(c *C) { - stdout, stderr, err := run() + stdout, stderr, err := run(nil) c.Check(err, IsNil) c.Check(string(stdout), Equals, "test stdout") c.Check(string(stderr), Equals, "test stderr") @@ -101,7 +104,7 @@ os.Args = []string{"snapctl", "foo", "--bar"} s.expectedArgs = []string{"foo", "--bar"} - stdout, stderr, err := run() + stdout, stderr, err := run(nil) c.Check(err, IsNil) c.Check(string(stdout), Equals, "test stdout") c.Check(string(stderr), Equals, "test stderr") @@ -114,6 +117,14 @@ os.Args = []string{"snapctl", "-h"} s.expectedArgs = []string{"-h"} - _, _, err := run() + _, _, err := run(nil) + c.Check(err, IsNil) +} + +func (s *snapctlSuite) TestSnapctlWithStdin(c *C) { + s.expectedStdin = []byte("hello") + mockStdin := bytes.NewBuffer(s.expectedStdin) + + _, _, err := run(mockStdin) c.Check(err, IsNil) } diff -Nru snapd-2.48+20.04/cmd/snap-repair/runner.go snapd-2.48.3+20.04/cmd/snap-repair/runner.go --- snapd-2.48+20.04/cmd/snap-repair/runner.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-repair/runner.go 2021-02-02 08:21:12.000000000 +0000 @@ -305,9 +305,9 @@ }, )) - peekRetryStrategy = retry.LimitCount(5, retry.LimitTime(44*time.Second, + peekRetryStrategy = retry.LimitCount(6, retry.LimitTime(44*time.Second, retry.Exponential{ - Initial: 300 * time.Millisecond, + Initial: 500 * time.Millisecond, Factor: 2.5, }, )) diff -Nru snapd-2.48+20.04/cmd/snap-update-ns/sorting.go snapd-2.48.3+20.04/cmd/snap-update-ns/sorting.go --- snapd-2.48+20.04/cmd/snap-update-ns/sorting.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-update-ns/sorting.go 2021-02-02 08:21:12.000000000 +0000 @@ -38,11 +38,17 @@ iOrigin := iMe.XSnapdOrigin() jOrigin := jMe.XSnapdOrigin() - if iOrigin == "overname" && iOrigin != jOrigin { - // should ith element be created by 'overname' mapping, it is - // always sorted before jth element, if that one comes from - // layouts or content interface - return true + if iOrigin != jOrigin { + // overname entries should always be sorted first, before + // entries from layouts or content interface + if iOrigin == "overname" { + // overname ith element should be sorted before + return true + } + if jOrigin == "overname" { + // non-overname ith element should be sorted after + return false + } } iDir := c[i].Dir diff -Nru snapd-2.48+20.04/cmd/snap-update-ns/sorting_test.go snapd-2.48.3+20.04/cmd/snap-update-ns/sorting_test.go --- snapd-2.48+20.04/cmd/snap-update-ns/sorting_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/cmd/snap-update-ns/sorting_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -52,21 +52,59 @@ func (s *sortSuite) TestParallelInstancesAndSimple(c *C) { // Naively sorted entries. entries := []osutil.MountEntry{ - {Dir: "/a/b"}, {Dir: "/a/b-1"}, + {Dir: "/a/b", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/snap/bar/baz", Options: []string{osutil.XSnapdOriginLayout()}}, {Dir: "/snap/bar", Options: []string{osutil.XSnapdOriginOvername()}}, {Dir: "/a/b-1/3"}, - {Dir: "/foo/bar", Options: []string{osutil.XSnapdOriginOvername()}}, + {Dir: "/var/snap/bar", Options: []string{osutil.XSnapdOriginOvername()}}, {Dir: "/a/b/c"}, } sort.Sort(byOriginAndMagicDir(entries)) // Entries sorted as if they had a trailing slash. c.Assert(entries, DeepEquals, []osutil.MountEntry{ - {Dir: "/foo/bar", Options: []string{osutil.XSnapdOriginOvername()}}, {Dir: "/snap/bar", Options: []string{osutil.XSnapdOriginOvername()}}, + {Dir: "/var/snap/bar", Options: []string{osutil.XSnapdOriginOvername()}}, {Dir: "/a/b-1"}, {Dir: "/a/b-1/3"}, - {Dir: "/a/b"}, + {Dir: "/a/b", Options: []string{osutil.XSnapdOriginLayout()}}, {Dir: "/a/b/c"}, + {Dir: "/snap/bar/baz", Options: []string{osutil.XSnapdOriginLayout()}}, + }) +} + +func (s *sortSuite) TestParallelInstancesAlmostSorted(c *C) { + // use a mount profile that was seen to be broken in the wild + entries := []osutil.MountEntry{ + {Dir: "/snap/foo", Options: []string{osutil.XSnapdOriginOvername()}}, + {Dir: "/var/snap/foo", Options: []string{osutil.XSnapdOriginOvername()}}, + {Dir: "/snap/foo/44/foo/certs", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/snap/foo/44/foo/config", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/snap/foo/44/usr/bin/python", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/snap/foo/44/usr/bin/python3", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/java", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/java8", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/node", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/nodejs12x", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/python2.7", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/python3.7", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/python3.8", Options: []string{osutil.XSnapdOriginLayout()}}, + } + sort.Sort(byOriginAndMagicDir(entries)) + // overname entries are always first + c.Assert(entries, DeepEquals, []osutil.MountEntry{ + {Dir: "/snap/foo", Options: []string{osutil.XSnapdOriginOvername()}}, + {Dir: "/var/snap/foo", Options: []string{osutil.XSnapdOriginOvername()}}, + {Dir: "/snap/foo/44/foo/certs", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/snap/foo/44/foo/config", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/snap/foo/44/usr/bin/python", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/snap/foo/44/usr/bin/python3", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/java", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/java8", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/node", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/nodejs12x", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/python2.7", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/python3.7", Options: []string{osutil.XSnapdOriginLayout()}}, + {Dir: "/usr/bin/python3.8", Options: []string{osutil.XSnapdOriginLayout()}}, }) } diff -Nru snapd-2.48+20.04/daemon/api.go snapd-2.48.3+20.04/daemon/api.go --- snapd-2.48+20.04/daemon/api.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/daemon/api.go 2021-02-02 08:21:12.000000000 +0000 @@ -2158,12 +2158,13 @@ } func runSnapctl(c *Command, r *http.Request, user *auth.UserState) Response { - var snapctlOptions client.SnapCtlOptions - if err := jsonutil.DecodeWithNumber(r.Body, &snapctlOptions); err != nil { + var snapctlPostData client.SnapCtlPostData + + if err := jsonutil.DecodeWithNumber(r.Body, &snapctlPostData); err != nil { return BadRequest("cannot decode snapctl request: %s", err) } - if len(snapctlOptions.Args) == 0 { + if len(snapctlPostData.Args) == 0 { return BadRequest("snapctl cannot run without args") } @@ -2174,8 +2175,17 @@ // Ignore missing context error to allow 'snapctl -h' without a context; // Actual context is validated later by get/set. - context, _ := c.d.overlord.HookManager().Context(snapctlOptions.ContextID) - stdout, stderr, err := ctlcmdRun(context, snapctlOptions.Args, uid) + context, _ := c.d.overlord.HookManager().Context(snapctlPostData.ContextID) + + // make the data read from stdin available for the hook + // TODO: use a forwarded stdin here + if snapctlPostData.Stdin != nil { + context.Lock() + context.Set("stdin", snapctlPostData.Stdin) + context.Unlock() + } + + stdout, stderr, err := ctlcmdRun(context, snapctlPostData.Args, uid) if err != nil { if e, ok := err.(*ctlcmd.UnsuccessfulError); ok { result := map[string]interface{}{ diff -Nru snapd-2.48+20.04/daemon/api_systems.go snapd-2.48.3+20.04/daemon/api_systems.go --- snapd-2.48+20.04/daemon/api_systems.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/daemon/api_systems.go 2021-02-02 08:21:12.000000000 +0000 @@ -33,6 +33,13 @@ var systemsCmd = &Command{ Path: "/v2/systems", GET: getSystems, + // this is awkward, we want the postSystemsAction function to be used + // when the label is empty too, but the router will not handle the request + // for /v2/systems with the systemsActionCmd and instead handles it through + // this command, so we need to set the POST for this command to essentially + // forward to that one + POST: postSystemsAction, + RootOnly: true, } var systemsActionCmd = &Command{ diff -Nru snapd-2.48+20.04/debian/changelog snapd-2.48.3+20.04/debian/changelog --- snapd-2.48+20.04/debian/changelog 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/debian/changelog 2021-02-02 08:21:12.000000000 +0000 @@ -1,4 +1,69 @@ -snapd (2.48+20.04) focal; urgency=medium +snapd (2.48.3+20.04) focal-security; urgency=medium + + * SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + * interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + + -- Michael Vogt Tue, 02 Feb 2021 09:21:12 +0100 + +snapd (2.48.2) xenial; urgency=medium + + * New upstream release, LP: #1906690 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + + -- Michael Vogt Tue, 15 Dec 2020 20:21:44 +0100 + +snapd (2.48.1) xenial; urgency=medium + + * New upstream release, LP: #1906690 + - gadget: disable ubuntu-boot role validation check + + -- Michael Vogt Thu, 03 Dec 2020 17:43:30 +0100 + +snapd (2.48) xenial; urgency=medium * New upstream release, LP: #1904098 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/gadget/gadget.go snapd-2.48.3+20.04/gadget/gadget.go --- snapd-2.48+20.04/gadget/gadget.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/gadget/gadget.go 2021-02-02 08:21:12.000000000 +0000 @@ -625,8 +625,11 @@ var ( reservedLabels = []string{ - ubuntuBootLabel, ubuntuSeedLabel, - ubuntuDataLabel, ubuntuSaveLabel, + // 2020-12-02 disabled because of customer gadget hotfix + /*ubuntuBootLabel,*/ + ubuntuSeedLabel, + ubuntuDataLabel, + ubuntuSaveLabel, } ) diff -Nru snapd-2.48+20.04/gadget/gadget_test.go snapd-2.48.3+20.04/gadget/gadget_test.go --- snapd-2.48+20.04/gadget/gadget_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/gadget/gadget_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -1296,7 +1296,8 @@ role, label, err string }{ {label: "ubuntu-seed", err: `label "ubuntu-seed" is reserved`}, - {label: "ubuntu-boot", err: `label "ubuntu-boot" is reserved`}, + // 2020-12-02: disable for customer hotfix + /*{label: "ubuntu-boot", err: `label "ubuntu-boot" is reserved`},*/ {label: "ubuntu-data", err: `label "ubuntu-data" is reserved`}, {label: "ubuntu-save", err: `label "ubuntu-save" is reserved`}, // these are ok diff -Nru snapd-2.48+20.04/.github/workflows/test.yaml snapd-2.48.3+20.04/.github/workflows/test.yaml --- snapd-2.48+20.04/.github/workflows/test.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/.github/workflows/test.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -286,7 +286,9 @@ echo "::set-output name=already-ran::true" fi - name: Run spread tests - if: "contains(github.event.pull_request.labels.*.name, 'Run nested') && steps.cached-results.outputs.already-ran != 'true'" + # run if the commit is pushed to the release/* branch or there is a 'Run + # nested' label set on the PR + if: "(contains(github.event.pull_request.labels.*.name, 'Run nested') || contains(github.ref, 'refs/heads/release/')) && steps.cached-results.outputs.already-ran != 'true'" env: SPREAD_GOOGLE_KEY: ${{ secrets.SPREAD_GOOGLE_KEY }} run: | diff -Nru snapd-2.48+20.04/interfaces/builtin/all.go snapd-2.48.3+20.04/interfaces/builtin/all.go --- snapd-2.48+20.04/interfaces/builtin/all.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/all.go 2021-02-02 08:21:12.000000000 +0000 @@ -29,6 +29,15 @@ func init() { snap.SanitizePlugsSlots = SanitizePlugsSlots + + // setup the ByName function using allInterfaces + interfaces.ByName = func(name string) (interfaces.Interface, error) { + iface, ok := allInterfaces[name] + if !ok { + return nil, fmt.Errorf("interface %q not found", name) + } + return iface, nil + } } var ( diff -Nru snapd-2.48+20.04/interfaces/builtin/common.go snapd-2.48.3+20.04/interfaces/builtin/common.go --- snapd-2.48+20.04/interfaces/builtin/common.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/common.go 2021-02-02 08:21:12.000000000 +0000 @@ -69,6 +69,8 @@ suppressPtraceTrace bool suppressHomeIx bool controlsDeviceCgroup bool + + serviceSnippets []string } // Name returns the interface name. @@ -88,6 +90,10 @@ } } +func (iface *commonInterface) ServicePermanentPlug(plug *snap.PlugInfo) []string { + return iface.serviceSnippets +} + func (iface *commonInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { if iface.usesPtraceTrace { spec.SetUsesPtraceTrace() diff -Nru snapd-2.48+20.04/interfaces/builtin/docker_support.go snapd-2.48.3+20.04/interfaces/builtin/docker_support.go --- snapd-2.48+20.04/interfaces/builtin/docker_support.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/docker_support.go 2021-02-02 08:21:12.000000000 +0000 @@ -26,7 +26,6 @@ "github.com/snapcore/snapd/interfaces/apparmor" "github.com/snapcore/snapd/interfaces/kmod" "github.com/snapcore/snapd/interfaces/seccomp" - "github.com/snapcore/snapd/interfaces/udev" "github.com/snapcore/snapd/release" apparmor_sandbox "github.com/snapcore/snapd/sandbox/apparmor" "github.com/snapcore/snapd/snap" @@ -74,10 +73,11 @@ /{,var/}run/runc/** mrwklix, # Allow sockets/etc for containerd -/{,var/}run/containerd/{,runc/,runc/k8s.io/,runc/k8s.io/*/} rw, +/{,var/}run/containerd/{,s/,runc/,runc/k8s.io/,runc/k8s.io/*/} rw, /{,var/}run/containerd/runc/k8s.io/*/** rwk, /{,var/}run/containerd/{io.containerd*/,io.containerd*/k8s.io/,io.containerd*/k8s.io/*/} rw, /{,var/}run/containerd/io.containerd*/*/** rwk, +/{,var/}run/containerd/s/** rwk, # Limit ipam-state to k8s /run/ipam-state/k8s-** rw, @@ -638,32 +638,16 @@ @unrestricted ` -type dockerSupportInterface struct{} +const dockerSupportServiceSnippet = `Delegate=true` -func (iface *dockerSupportInterface) Name() string { - return "docker-support" -} - -func (iface *dockerSupportInterface) StaticInfo() interfaces.StaticInfo { - return interfaces.StaticInfo{ - Summary: dockerSupportSummary, - ImplicitOnCore: true, - ImplicitOnClassic: true, - BaseDeclarationPlugs: dockerSupportBaseDeclarationPlugs, - BaseDeclarationSlots: dockerSupportBaseDeclarationSlots, - } +type dockerSupportInterface struct { + commonInterface } var ( parserFeatures = apparmor_sandbox.ParserFeatures ) -func (iface *dockerSupportInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { - spec.SetControlsDeviceCgroup() - - return nil -} - func (iface *dockerSupportInterface) KModConnectedPlug(spec *kmod.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { // https://kubernetes.io/docs/setup/production-environment/container-runtimes/ if err := spec.AddModule("overlay"); err != nil { @@ -716,5 +700,16 @@ } func init() { - registerIface(&dockerSupportInterface{}) + registerIface(&dockerSupportInterface{commonInterface{ + name: "docker-support", + summary: dockerSupportSummary, + implicitOnCore: true, + implicitOnClassic: true, + baseDeclarationPlugs: dockerSupportBaseDeclarationPlugs, + baseDeclarationSlots: dockerSupportBaseDeclarationSlots, + controlsDeviceCgroup: true, + serviceSnippets: []string{dockerSupportServiceSnippet}, + // docker-support also uses ptrace(trace), but it already declares this in + // the AppArmorConnectedPlug method + }}) } diff -Nru snapd-2.48+20.04/interfaces/builtin/docker_support_test.go snapd-2.48.3+20.04/interfaces/builtin/docker_support_test.go --- snapd-2.48+20.04/interfaces/builtin/docker_support_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/docker_support_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -48,6 +48,8 @@ privContainersPlug *interfaces.ConnectedPlug noPrivContainersPlugInfo *snap.PlugInfo noPrivContainersPlug *interfaces.ConnectedPlug + malformedPlugInfo *snap.PlugInfo + malformedPlug *interfaces.ConnectedPlug } const coreDockerSlotYaml = `name: core @@ -68,6 +70,19 @@ - network-control ` +const dockerSupportPrivilegedContainersMalformedMockPlugSnapInfoYaml = `name: docker +version: 1.0 +plugs: + privileged: + interface: docker-support + privileged-containers: foobar +apps: + app: + command: foo + plugs: + - privileged +` + const dockerSupportPrivilegedContainersFalseMockPlugSnapInfoYaml = `name: docker version: 1.0 plugs: @@ -105,6 +120,7 @@ s.networkCtrlSlot, s.networkCtrlSlotInfo = MockConnectedSlot(c, coreDockerSlotYaml, nil, "network-control") s.privContainersPlug, s.privContainersPlugInfo = MockConnectedPlug(c, dockerSupportPrivilegedContainersTrueMockPlugSnapInfoYaml, nil, "privileged") s.noPrivContainersPlug, s.noPrivContainersPlugInfo = MockConnectedPlug(c, dockerSupportPrivilegedContainersFalseMockPlugSnapInfoYaml, nil, "privileged") + s.malformedPlug, s.malformedPlugInfo = MockConnectedPlug(c, dockerSupportPrivilegedContainersMalformedMockPlugSnapInfoYaml, nil, "privileged") } func (s *DockerSupportInterfaceSuite) TestName(c *C) { @@ -243,3 +259,15 @@ c.Assert(spec.AddConnectedPlug(builtin.MustInterface("network-control"), s.networkCtrlPlug, s.networkCtrlSlot), IsNil) c.Assert(spec.Snippets(), HasLen, 0) } + +func (s *DockerSupportInterfaceSuite) TestServicePermanentPlugSnippets(c *C) { + snips, err := interfaces.PermanentPlugServiceSnippets(s.iface, s.plugInfo) + c.Assert(err, IsNil) + c.Check(snips, DeepEquals, []string{"Delegate=true"}) + + // check that a malformed plug with bad attribute returns non-nil error + // from PermanentPlugServiceSnippets, thereby ensuring that function + // sanitizes plugs + _, err = interfaces.PermanentPlugServiceSnippets(s.iface, s.malformedPlugInfo) + c.Assert(err, ErrorMatches, "docker-support plug requires bool with 'privileged-containers'") +} diff -Nru snapd-2.48+20.04/interfaces/builtin/greengrass_support.go snapd-2.48.3+20.04/interfaces/builtin/greengrass_support.go --- snapd-2.48+20.04/interfaces/builtin/greengrass_support.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/greengrass_support.go 2021-02-02 08:21:12.000000000 +0000 @@ -22,7 +22,10 @@ import ( "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" + "github.com/snapcore/snapd/interfaces/seccomp" + "github.com/snapcore/snapd/interfaces/udev" "github.com/snapcore/snapd/release" + "github.com/snapcore/snapd/snap" ) const greengrassSupportSummary = `allows operating as the Greengrass service` @@ -51,7 +54,18 @@ /system-data/var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/*/ggc-writable/{,**} rw, ` -const greengrassSupportConnectedPlugAppArmor = ` +const greengrassSupportProcessModeConnectedPlugAppArmor = ` +# Description: can manage greengrass 'things' and their sandboxes. This policy +# is meant currently only to enable Greengrass to run _only_ process-mode or +# "no container" lambdas. +# needed by older versions of cloneBinary.ensureSelfCloned() to avoid +# CVE-2019-5736 +/ ix, +# newer versions of runC have this denial instead of "/ ix" above +/bin/runc rix, +` + +const greengrassSupportFullContainerConnectedPlugAppArmor = ` # Description: can manage greengrass 'things' and their sandboxes. This # policy is intentionally not restrictive and is here to help guard against # programming errors and not for security confinement. The greengrassd @@ -379,14 +393,68 @@ mknodat - - |S_IFCHR - ` +func (iface *greengrassSupportInterface) ServicePermanentPlug(plug *snap.PlugInfo) []string { + var flavor string + _ = plug.Attr("flavor", &flavor) + + // only no-container flavor does not get Delegate=true, all other flavors + // (including no flavor, which is the same as legacy-container flavor) + // are usable to manage control groups of processes/containers, and thus + // need Delegate=true + if flavor == "no-container" { + return nil + } + + return []string{"Delegate=true"} +} + func (iface *greengrassSupportInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { - if release.OnClassic { - spec.AddSnippet(greengrassSupportConnectedPlugAppArmor) - } else { - spec.AddSnippet(greengrassSupportConnectedPlugAppArmor + greengrassSupportConnectedPlugAppArmorCore) + // check the flavor + var flavor string + _ = plug.Attr("flavor", &flavor) + switch flavor { + case "", "legacy-container": + // default, legacy version of the interface + if release.OnClassic { + spec.AddSnippet(greengrassSupportFullContainerConnectedPlugAppArmor) + } else { + spec.AddSnippet(greengrassSupportFullContainerConnectedPlugAppArmor + greengrassSupportConnectedPlugAppArmorCore) + } + // greengrass needs to use ptrace for controlling it's containers + spec.SetUsesPtraceTrace() + case "no-container": + // this is the no-container version, it does not use as much privilege + // as the default "legacy-container" flavor + spec.AddSnippet(greengrassSupportProcessModeConnectedPlugAppArmor) + } + + return nil +} + +func (iface *greengrassSupportInterface) SecCompConnectedPlug(spec *seccomp.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { + // check the flavor + var flavor string + _ = plug.Attr("flavor", &flavor) + switch flavor { + case "", "legacy-container": + spec.AddSnippet(greengrassSupportConnectedPlugSeccomp) + case "no-container": + // no-container has no additional seccomp available to it + } + + return nil +} + +func (iface *greengrassSupportInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { + var flavor string + _ = plug.Attr("flavor", &flavor) + switch flavor { + case "", "legacy-container": + // default containerization controls the device cgroup + spec.SetControlsDeviceCgroup() + case "no-container": + // no-container does not control the device cgroup } - // greengrass needs to use ptrace - spec.SetUsesPtraceTrace() return nil } @@ -404,7 +472,5 @@ implicitOnClassic: true, baseDeclarationSlots: greengrassSupportBaseDeclarationSlots, baseDeclarationPlugs: greengrassSupportBaseDeclarationPlugs, - connectedPlugSecComp: greengrassSupportConnectedPlugSeccomp, - controlsDeviceCgroup: true, }}) } diff -Nru snapd-2.48+20.04/interfaces/builtin/greengrass_support_test.go snapd-2.48.3+20.04/interfaces/builtin/greengrass_support_test.go --- snapd-2.48+20.04/interfaces/builtin/greengrass_support_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/greengrass_support_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -42,6 +42,14 @@ extraSlot *interfaces.ConnectedSlot extraPlugInfo *snap.PlugInfo extraPlug *interfaces.ConnectedPlug + + // for the process flavor + processModePlugInfo *snap.PlugInfo + processModePlug *interfaces.ConnectedPlug + + // for the container flavor + containerModePlugInfo *snap.PlugInfo + containerModePlug *interfaces.ConnectedPlug } const coreSlotYaml = `name: core @@ -53,10 +61,26 @@ ` const ggMockPlugSnapInfoYaml = `name: other version: 1.0 +plugs: + greengrass-support-legacy-container: + interface: greengrass-support + flavor: legacy-container +apps: + app2: + command: foo + plugs: [greengrass-support-legacy-container, greengrass-support, network-control] +` + +const ggProcessModeMockPlugSnapInfoYaml = `name: other +version: 1.0 +plugs: + greengrass-support-no-container: + interface: greengrass-support + flavor: no-container apps: app2: command: foo - plugs: [greengrass-support, network-control] + plugs: [greengrass-support-no-container, network-control] ` var _ = Suite(&GreengrassSupportInterfaceSuite{ @@ -69,6 +93,10 @@ s.extraPlug, s.extraPlugInfo = MockConnectedPlug(c, ggMockPlugSnapInfoYaml, nil, "network-control") s.extraSlot, s.extraSlotInfo = MockConnectedSlot(c, coreSlotYaml, nil, "network-control") + s.processModePlug, s.processModePlugInfo = MockConnectedPlug(c, ggProcessModeMockPlugSnapInfoYaml, nil, "greengrass-support-no-container") + + s.containerModePlug, s.containerModePlugInfo = MockConnectedPlug(c, ggMockPlugSnapInfoYaml, nil, "greengrass-support-legacy-container") + } func (s *GreengrassSupportInterfaceSuite) TestName(c *C) { @@ -84,39 +112,86 @@ } func (s *GreengrassSupportInterfaceSuite) TestAppArmorSpec(c *C) { + + for _, plug := range []*interfaces.ConnectedPlug{ + s.plug, + s.containerModePlug, + } { + spec := &apparmor.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, plug, s.slot), IsNil) + c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.other.app2"}) + c.Check(spec.SnippetForTag("snap.other.app2"), testutil.Contains, "mount options=(rw, bind) /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/** -> /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/** ,\n") + c.Check(spec.UsesPtraceTrace(), Equals, true) + } +} + +func (s *GreengrassSupportInterfaceSuite) TestProcessModeAppArmorSpec(c *C) { spec := &apparmor.Specification{} - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil) + c.Assert(spec.AddConnectedPlug(s.iface, s.processModePlug, s.slot), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.other.app2"}) - c.Check(spec.SnippetForTag("snap.other.app2"), testutil.Contains, "mount options=(rw, bind) /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/** -> /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/** ,\n") - c.Check(spec.UsesPtraceTrace(), Equals, true) + c.Check(spec.SnippetForTag("snap.other.app2"), testutil.Contains, "/ ix,\n") + c.Check(spec.SnippetForTag("snap.other.app2"), Not(testutil.Contains), "mount options=(rw, bind) /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/** -> /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/** ,\n") + c.Check(spec.UsesPtraceTrace(), Equals, false) } func (s *GreengrassSupportInterfaceSuite) TestSecCompSpec(c *C) { + for _, plug := range []*interfaces.ConnectedPlug{ + s.plug, + s.containerModePlug, + } { + spec := &seccomp.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, plug, s.slot), IsNil) + c.Check(spec.SnippetForTag("snap.other.app2"), testutil.Contains, "# for overlayfs and various bind mounts\nmount\numount2\npivot_root\n") + } +} + +func (s *GreengrassSupportInterfaceSuite) TestProcessModeSecCompSpec(c *C) { spec := &seccomp.Specification{} - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil) - c.Check(spec.SnippetForTag("snap.other.app2"), testutil.Contains, "# for overlayfs and various bind mounts\nmount\numount2\npivot_root\n") + c.Assert(spec.AddConnectedPlug(s.iface, s.processModePlug, s.slot), IsNil) + c.Check(spec.SnippetForTag("snap.other.app2"), Not(testutil.Contains), "# for overlayfs and various bind mounts\nmount\numount2\npivot_root\n") } func (s *GreengrassSupportInterfaceSuite) TestUdevTaggingDisablingRemoveLast(c *C) { - // make a spec with network-control that has udev tagging - spec := &udev.Specification{} - c.Assert(spec.AddConnectedPlug(builtin.MustInterface("network-control"), s.extraPlug, s.extraSlot), IsNil) - c.Assert(spec.Snippets(), HasLen, 3) - - // connect the greengrass-support interface and ensure the spec is now nil - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil) - c.Check(spec.Snippets(), HasLen, 0) + for _, plug := range []*interfaces.ConnectedPlug{ + s.plug, + s.containerModePlug, + } { + // make a spec with network-control that has udev tagging + spec := &udev.Specification{} + c.Assert(spec.AddConnectedPlug(builtin.MustInterface("network-control"), s.extraPlug, s.extraSlot), IsNil) + c.Assert(spec.Snippets(), HasLen, 3) + + // connect the greengrass-support interface and ensure the spec is now nil + c.Assert(spec.AddConnectedPlug(s.iface, plug, s.slot), IsNil) + c.Check(spec.Snippets(), HasLen, 0) + } } -func (s *GreengrassSupportInterfaceSuite) TestUdevTaggingDisablingRemoveFirst(c *C) { +func (s *GreengrassSupportInterfaceSuite) TestProcessModeUdevTaggingWorks(c *C) { spec := &udev.Specification{} // connect the greengrass-support interface and ensure the spec is nil - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil) + c.Assert(spec.AddConnectedPlug(s.iface, s.processModePlug, s.slot), IsNil) c.Check(spec.Snippets(), HasLen, 0) - // add network-control and ensure the spec is still nil + // add network-control and now the spec is not nil c.Assert(spec.AddConnectedPlug(builtin.MustInterface("network-control"), s.extraPlug, s.extraSlot), IsNil) - c.Assert(spec.Snippets(), HasLen, 0) + c.Assert(spec.Snippets(), Not(HasLen), 0) +} + +func (s *GreengrassSupportInterfaceSuite) TestUdevTaggingDisablingRemoveFirst(c *C) { + for _, plug := range []*interfaces.ConnectedPlug{ + s.plug, + s.containerModePlug, + } { + spec := &udev.Specification{} + // connect the greengrass-support interface and ensure the spec is nil + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil) + c.Check(spec.Snippets(), HasLen, 0) + + // add network-control and ensure the spec is still nil + c.Assert(spec.AddConnectedPlug(builtin.MustInterface("network-control"), plug, s.extraSlot), IsNil) + c.Assert(spec.Snippets(), HasLen, 0) + } } func (s *GreengrassSupportInterfaceSuite) TestInterfaces(c *C) { @@ -127,24 +202,50 @@ restore := release.MockOnClassic(false) defer restore() - apparmorSpec := &apparmor.Specification{} - err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.slot) - c.Assert(err, IsNil) - c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app2"}) - - // verify core rule present - c.Check(apparmorSpec.SnippetForTag("snap.other.app2"), testutil.Contains, "# /system-data/var/snap/greengrass/x1/ggc-writable/packages/1.7.0/var/worker/overlays/$UUID/upper/\n") + for _, plug := range []*interfaces.ConnectedPlug{ + s.plug, + s.containerModePlug, + } { + apparmorSpec := &apparmor.Specification{} + err := apparmorSpec.AddConnectedPlug(s.iface, plug, s.slot) + c.Assert(err, IsNil) + c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app2"}) + + // verify core rule present + c.Check(apparmorSpec.SnippetForTag("snap.other.app2"), testutil.Contains, "# /system-data/var/snap/greengrass/x1/ggc-writable/packages/1.7.0/var/worker/overlays/$UUID/upper/\n") + } } func (s *GreengrassSupportInterfaceSuite) TestPermanentSlotAppArmorSessionClassic(c *C) { restore := release.MockOnClassic(true) defer restore() - apparmorSpec := &apparmor.Specification{} - err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.slot) - c.Assert(err, IsNil) - c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app2"}) - - // verify core rule not present - c.Check(apparmorSpec.SnippetForTag("snap.other.app2"), Not(testutil.Contains), "# /system-data/var/snap/greengrass/x1/ggc-writable/packages/1.7.0/var/worker/overlays/$UUID/upper/\n") + for _, plug := range []*interfaces.ConnectedPlug{ + s.plug, + s.containerModePlug, + } { + apparmorSpec := &apparmor.Specification{} + err := apparmorSpec.AddConnectedPlug(s.iface, plug, s.slot) + c.Assert(err, IsNil) + c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app2"}) + + // verify core rule not present + c.Check(apparmorSpec.SnippetForTag("snap.other.app2"), Not(testutil.Contains), "# /system-data/var/snap/greengrass/x1/ggc-writable/packages/1.7.0/var/worker/overlays/$UUID/upper/\n") + } +} + +func (s *GreengrassSupportInterfaceSuite) TestPermanentPlugServiceSnippets(c *C) { + for _, t := range []struct { + plug *snap.PlugInfo + exp []string + }{ + {s.plugInfo, []string{"Delegate=true"}}, + {s.containerModePlugInfo, []string{"Delegate=true"}}, + // the process-mode or no-container plug doesn't get Delegate=true + {s.processModePlugInfo, nil}, + } { + snips, err := interfaces.PermanentPlugServiceSnippets(s.iface, t.plug) + c.Assert(err, IsNil) + c.Check(snips, DeepEquals, t.exp) + } } diff -Nru snapd-2.48+20.04/interfaces/builtin/kubernetes_support.go snapd-2.48.3+20.04/interfaces/builtin/kubernetes_support.go --- snapd-2.48+20.04/interfaces/builtin/kubernetes_support.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/kubernetes_support.go 2021-02-02 08:21:12.000000000 +0000 @@ -277,7 +277,19 @@ commonInterface } -func k8sFlavor(plug *interfaces.ConnectedPlug) string { +func (iface *kubernetesSupportInterface) ServicePermanentPlug(plug *snap.PlugInfo) []string { + // only autobind-unix flavor does not get Delegate=true, all other flavors + // are usable to manage control groups of processes/containers, and thus + // need Delegate=true + flavor := k8sFlavor(plug) + if flavor == "autobind-unix" { + return nil + } + + return []string{"Delegate=true"} +} + +func k8sFlavor(plug interfaces.Attrer) string { var flavor string _ = plug.Attr("flavor", &flavor) return flavor diff -Nru snapd-2.48+20.04/interfaces/builtin/kubernetes_support_test.go snapd-2.48.3+20.04/interfaces/builtin/kubernetes_support_test.go --- snapd-2.48+20.04/interfaces/builtin/kubernetes_support_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/kubernetes_support_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -272,3 +272,20 @@ func (s *KubernetesSupportInterfaceSuite) TestInterfaces(c *C) { c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) } + +func (s *KubernetesSupportInterfaceSuite) TestPermanentPlugServiceSnippets(c *C) { + for _, t := range []struct { + plug *snap.PlugInfo + exp []string + }{ + {s.plugInfo, []string{"Delegate=true"}}, + {s.plugKubeletInfo, []string{"Delegate=true"}}, + {s.plugKubeproxyInfo, []string{"Delegate=true"}}, + // only autobind-unix flavor does not get Delegate=true + {s.plugKubeAutobindInfo, nil}, + } { + snips, err := interfaces.PermanentPlugServiceSnippets(s.iface, t.plug) + c.Assert(err, IsNil) + c.Check(snips, DeepEquals, t.exp) + } +} diff -Nru snapd-2.48+20.04/interfaces/builtin/lxd_support.go snapd-2.48.3+20.04/interfaces/builtin/lxd_support.go --- snapd-2.48+20.04/interfaces/builtin/lxd_support.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/lxd_support.go 2021-02-02 08:21:12.000000000 +0000 @@ -52,6 +52,8 @@ @unrestricted ` +const lxdSupportServiceSnippet = `Delegate=true` + func init() { registerIface(&commonInterface{ name: "lxd-support", @@ -62,5 +64,6 @@ baseDeclarationPlugs: lxdSupportBaseDeclarationPlugs, connectedPlugAppArmor: lxdSupportConnectedPlugAppArmor, connectedPlugSecComp: lxdSupportConnectedPlugSecComp, + serviceSnippets: []string{lxdSupportServiceSnippet}, }) } diff -Nru snapd-2.48+20.04/interfaces/builtin/lxd_support_test.go snapd-2.48.3+20.04/interfaces/builtin/lxd_support_test.go --- snapd-2.48+20.04/interfaces/builtin/lxd_support_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/lxd_support_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -103,3 +103,9 @@ func (s *LxdSupportInterfaceSuite) TestInterfaces(c *C) { c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) } + +func (s *LxdSupportInterfaceSuite) TestServicePermanentPlugSnippets(c *C) { + snips, err := interfaces.PermanentPlugServiceSnippets(s.iface, s.plugInfo) + c.Assert(err, IsNil) + c.Check(snips, DeepEquals, []string{"Delegate=true"}) +} diff -Nru snapd-2.48+20.04/interfaces/builtin/system_observe.go snapd-2.48.3+20.04/interfaces/builtin/system_observe.go --- snapd-2.48+20.04/interfaces/builtin/system_observe.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/builtin/system_observe.go 2021-02-02 08:21:12.000000000 +0000 @@ -117,6 +117,13 @@ interface=org.freedesktop.DBus.Peer member=GetMachineId peer=(label=unconfined), + +# Allow reading if protected hardlinks are enabled, but don't allow enabling or +# disabling them +@{PROC}/sys/fs/protected_hardlinks r, +@{PROC}/sys/fs/protected_symlinks r, +@{PROC}/sys/fs/protected_fifos r, +@{PROC}/sys/fs/protected_regular r, ` const systemObserveConnectedPlugSecComp = ` diff -Nru snapd-2.48+20.04/interfaces/core.go snapd-2.48.3+20.04/interfaces/core.go --- snapd-2.48+20.04/interfaces/core.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/core.go 2021-02-02 08:21:12.000000000 +0000 @@ -27,7 +27,7 @@ "github.com/snapcore/snapd/snap" ) -// Sanitize plug with a given snapd interface. +// BeforePreparePlug sanitizes a plug with a given snapd interface. func BeforePreparePlug(iface Interface, plugInfo *snap.PlugInfo) error { if iface.Name() != plugInfo.Interface { return fmt.Errorf("cannot sanitize plug %q (interface %q) using interface %q", @@ -40,6 +40,13 @@ return err } +// ByName returns an Interface for the given interface name. Note that in order for +// this to work properly, the package "interfaces/builtin" must also eventually be +// imported to populate the full list of interfaces. +var ByName = func(name string) (iface Interface, err error) { + panic("ByName is unset, import interfaces/builtin to initialize this") +} + // PlugRef is a reference to a plug. type PlugRef struct { Snap string `json:"snap"` @@ -197,6 +204,27 @@ BaseDeclarationSlots string } +// PermanentPlugServiceSnippets will return the set of snippets for the systemd +// service unit that should be generated for a snap with the specified plug. +// The list returned is not unique, callers must de-duplicate themselves. +// The plug is provided because the snippet may depend on plug attributes for +// example. The plug is sanitized before the snippets are returned. +func PermanentPlugServiceSnippets(iface Interface, plug *snap.PlugInfo) (snips []string, err error) { + // sanitize the plug first + err = BeforePreparePlug(iface, plug) + if err != nil { + return nil, err + } + + type serviceSnippetPlugger interface { + ServicePermanentPlug(plug *snap.PlugInfo) []string + } + if iface, ok := iface.(serviceSnippetPlugger); ok { + snips = iface.ServicePermanentPlug(plug) + } + return snips, nil +} + // StaticInfoOf returns the static-info of the given interface. func StaticInfoOf(iface Interface) (si StaticInfo) { type metaDataProvider interface { diff -Nru snapd-2.48+20.04/interfaces/core_test.go snapd-2.48.3+20.04/interfaces/core_test.go --- snapd-2.48+20.04/interfaces/core_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/interfaces/core_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -25,7 +25,10 @@ . "gopkg.in/check.v1" + "github.com/snapcore/snapd/interfaces" + // TODO: eliminate this import and just use interfaces import directly . "github.com/snapcore/snapd/interfaces" + "github.com/snapcore/snapd/interfaces/builtin" "github.com/snapcore/snapd/interfaces/ifacetest" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" @@ -145,6 +148,98 @@ c.Assert(err, ErrorMatches, `malformed connection identifier: ".*"`) } +type simpleIface struct { + name string +} + +func (si simpleIface) Name() string { return si.name } +func (si simpleIface) AutoConnect(plug *snap.PlugInfo, slot *snap.SlotInfo) bool { return false } + +func (s *CoreSuite) TestByName(c *C) { + // setup a mock interface using builtin - this will also trigger init() in + // builtin package which set ByName to a real implementation + r := builtin.MockInterface(simpleIface{name: "mock-network"}) + defer r() + + _, err := interfaces.ByName("no-such-interface") + c.Assert(err, ErrorMatches, "interface \"no-such-interface\" not found") + + iface, err := interfaces.ByName("mock-network") + c.Assert(err, IsNil) + c.Assert(iface.Name(), Equals, "mock-network") +} + +type serviceSnippetIface struct { + simpleIface + + sanitizerErr error + + snips []string +} + +func (ssi serviceSnippetIface) BeforePreparePlug(plug *snap.PlugInfo) error { + return ssi.sanitizerErr +} + +func (ssi serviceSnippetIface) ServicePermanentPlug(plug *snap.PlugInfo) []string { + return ssi.snips +} + +func (s *CoreSuite) TestPermanentPlugServiceSnippets(c *C) { + // setup a mock interface using builtin - this will also trigger init() in + // builtin package which set ByName to a real implementation + ssi := serviceSnippetIface{ + simpleIface: simpleIface{name: "mock-service-snippets"}, + snips: []string{"foo1", "foo2"}, + } + r := builtin.MockInterface(ssi) + defer r() + + iface, err := interfaces.ByName("mock-service-snippets") + c.Assert(err, IsNil) + c.Assert(iface.Name(), Equals, "mock-service-snippets") + + info := snaptest.MockInfo(c, ` +name: snap +version: 0 +plugs: + plug: + interface: mock-service-snippets +`, nil) + plug := info.Plugs["plug"] + + snips, err := interfaces.PermanentPlugServiceSnippets(iface, plug) + c.Assert(err, IsNil) + c.Assert(snips, DeepEquals, []string{"foo1", "foo2"}) +} + +func (s *CoreSuite) TestPermanentPlugServiceSnippetsSanitizesPlugs(c *C) { + // setup a mock interface using builtin - this will also trigger init() in + // builtin package which set ByName to a real implementation + ssi := serviceSnippetIface{ + simpleIface: simpleIface{name: "unclean-service-snippets"}, + sanitizerErr: fmt.Errorf("cannot sanitize: foo"), + } + r := builtin.MockInterface(ssi) + defer r() + + info := snaptest.MockInfo(c, ` +name: snap +version: 0 +plugs: + plug: + interface: unclean-service-snippets +`, nil) + plug := info.Plugs["plug"] + + iface, err := interfaces.ByName("unclean-service-snippets") + c.Assert(err, IsNil) + c.Assert(iface.Name(), Equals, "unclean-service-snippets") + + _, err = interfaces.PermanentPlugServiceSnippets(iface, plug) + c.Assert(err, ErrorMatches, "cannot sanitize: foo") +} + func (s *CoreSuite) TestSanitizePlug(c *C) { info := snaptest.MockInfo(c, ` name: snap diff -Nru snapd-2.48+20.04/overlord/devicestate/devicemgr.go snapd-2.48.3+20.04/overlord/devicestate/devicemgr.go --- snapd-2.48+20.04/overlord/devicestate/devicemgr.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/devicemgr.go 2021-02-02 08:21:12.000000000 +0000 @@ -20,6 +20,8 @@ package devicestate import ( + "context" + "encoding/json" "errors" "fmt" "os" @@ -38,6 +40,7 @@ "github.com/snapcore/snapd/overlord/assertstate" "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/configstate/config" + "github.com/snapcore/snapd/overlord/devicestate/fde" "github.com/snapcore/snapd/overlord/devicestate/internal" "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/snapstate" @@ -45,6 +48,7 @@ "github.com/snapcore/snapd/overlord/storecontext" "github.com/snapcore/snapd/progress" "github.com/snapcore/snapd/release" + "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snapdenv" "github.com/snapcore/snapd/sysconfig" "github.com/snapcore/snapd/systemd" @@ -67,7 +71,8 @@ // save as rw vs ro, or mount/umount it fully on demand saveAvailable bool - state *state.State + state *state.State + hookMgr *hookstate.HookManager cachedKeypairMgr asserts.KeypairManager @@ -99,6 +104,7 @@ m := &DeviceManager{ state: s, + hookMgr: hookManager, newStore: newStore, reg: make(chan struct{}), preseed: snapdenv.Preseeding(), @@ -144,6 +150,8 @@ // wire FDE kernel hook support into boot boot.HasFDESetupHook = m.hasFDESetupHook + boot.RunFDESetupHook = m.runFDESetupHook + hookManager.Register(regexp.MustCompile("^fde-setup$"), newFdeSetupHandler) return m, nil } @@ -1194,7 +1202,7 @@ systemMode := m.SystemMode() currentSys, err := currentSystemForMode(m.state, systemMode) if err != nil { - return fmt.Errorf("cannot get curent system: %v", err) + return fmt.Errorf("cannot get current system: %v", err) } systemLabel = currentSys.System } @@ -1358,6 +1366,7 @@ } func (m *DeviceManager) hasFDESetupHook() (bool, error) { + // state must be locked st := m.state deviceCtx, err := DeviceCtx(st, nil, nil) @@ -1369,6 +1378,115 @@ if err != nil { return false, fmt.Errorf("cannot get kernel info: %v", err) } + return hasFDESetupHookInKernel(kernelInfo), nil +} + +func (m *DeviceManager) runFDESetupHook(op string, params *boot.FDESetupHookParams) ([]byte, error) { + // TODO:UC20: when this runs on refresh we need to be very careful + // that we never run this when the kernel is not fully configured + // i.e. when there are no security profiles for the hook + + // state must be locked + st := m.state + + deviceCtx, err := DeviceCtx(st, nil, nil) + if err != nil { + return nil, fmt.Errorf("cannot get device context to run fde-setup hook: %v", err) + } + kernelInfo, err := snapstate.KernelInfo(st, deviceCtx) + if err != nil { + return nil, fmt.Errorf("cannot get kernel info to run fde-setup hook: %v", err) + } + hooksup := &hookstate.HookSetup{ + Snap: kernelInfo.InstanceName(), + Revision: kernelInfo.Revision, + Hook: "fde-setup", + // XXX: should this be configurable somehow? + Timeout: 5 * time.Minute, + } + req := &fde.SetupRequest{ + Op: op, + Key: ¶ms.Key, + KeyName: params.KeyName, + // TODO: include boot chains + } + for _, model := range params.Models { + req.Models = append(req.Models, map[string]string{ + "series": model.Series(), + "brand-id": model.BrandID(), + "model": model.Model(), + "grade": string(model.Grade()), + "signkey-id": model.SignKeyID(), + }) + } + contextData := map[string]interface{}{ + "fde-setup-request": req, + } + st.Unlock() + defer st.Lock() + context, err := m.hookMgr.EphemeralRunHook(context.Background(), hooksup, contextData) + if err != nil { + return nil, fmt.Errorf("cannot run hook for %q: %v", op, err) + } + // the hook is expected to call "snapctl fde-setup-result" which + // wil set the "fde-setup-result" value on the task + var hookResult []byte + context.Lock() + err = context.Get("fde-setup-result", &hookResult) + context.Unlock() + if err != nil { + return nil, fmt.Errorf("cannot get result from fde-setup hook %q: %v", op, err) + } + + return hookResult, nil +} + +func (m *DeviceManager) checkFDEFeatures(st *state.State) error { + // Run fde-setup hook with "op":"features". If the hook + // returns any {"features":[...]} reply we consider the + // hardware supported. If the hook errors or if it returns + // {"error":"hardware-unsupported"} we don't. + output, err := m.runFDESetupHook("features", &boot.FDESetupHookParams{}) + if err != nil { + return err + } + var res struct { + Features []string `json:"features"` + Error string `json:"error"` + } + if err := json.Unmarshal(output, &res); err != nil { + return fmt.Errorf("cannot parse hook output %q: %v", output, err) + } + if res.Features == nil && res.Error == "" { + return fmt.Errorf(`cannot use hook: neither "features" nor "error" returned`) + } + if res.Error != "" { + return fmt.Errorf("cannot use hook: it returned error: %v", res.Error) + } + return nil +} + +func hasFDESetupHookInKernel(kernelInfo *snap.Info) bool { _, ok := kernelInfo.Hooks["fde-setup"] - return ok, nil + return ok +} + +type fdeSetupHandler struct { + context *hookstate.Context +} + +func newFdeSetupHandler(ctx *hookstate.Context) hookstate.Handler { + return fdeSetupHandler{context: ctx} +} + +func (h fdeSetupHandler) Before() error { + return nil +} + +func (h fdeSetupHandler) Done() error { + return nil +} + +func (h fdeSetupHandler) Error(err error) error { + return nil } diff -Nru snapd-2.48+20.04/overlord/devicestate/devicestate_install_mode_test.go snapd-2.48.3+20.04/overlord/devicestate/devicestate_install_mode_test.go --- snapd-2.48+20.04/overlord/devicestate/devicestate_install_mode_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/devicestate_install_mode_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -27,6 +27,7 @@ "path/filepath" . "gopkg.in/check.v1" + "gopkg.in/tomb.v2" "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/boot" @@ -35,10 +36,13 @@ "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/gadget" "github.com/snapcore/snapd/gadget/install" + "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/devicestate" "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" + "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/snapstate" + "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" "github.com/snapcore/snapd/overlord/state" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/secboot" @@ -87,12 +91,13 @@ s.state.Set("seeded", true) } +const ( + pcSnapID = "pcididididididididididididididid" + pcKernelSnapID = "pckernelidididididididididididid" + core20SnapID = "core20ididididididididididididid" +) + func (s *deviceMgrInstallModeSuite) makeMockInstalledPcGadget(c *C, grade, gadgetDefaultsYaml string) *asserts.Model { - const ( - pcSnapID = "pcididididididididididididididid" - pcKernelSnapID = "pckernelidididididididididididid" - core20SnapID = "core20ididididididididididididid" - ) si := &snap.SideInfo{ RealName: "pc-kernel", Revision: snap.R(1), @@ -439,7 +444,7 @@ func (s *deviceMgrInstallModeSuite) TestInstallSecured(c *C) { err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: false, bypass: false, encrypt: false}) - c.Assert(err, ErrorMatches, "(?s).*cannot encrypt secured device: TPM not available.*") + c.Assert(err, ErrorMatches, "(?s).*cannot encrypt device storage as mandated by model grade secured:.*TPM not available.*") } func (s *deviceMgrInstallModeSuite) TestInstallSecuredWithTPM(c *C) { @@ -481,7 +486,7 @@ func (s *deviceMgrInstallModeSuite) TestInstallSecuredBypassEncryption(c *C) { err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: false, bypass: true, encrypt: false}) - c.Assert(err, ErrorMatches, "(?s).*cannot encrypt secured device: TPM not available.*") + c.Assert(err, ErrorMatches, "(?s).*cannot encrypt device storage as mandated by model grade secured:.*TPM not available.*") } func (s *deviceMgrInstallModeSuite) testInstallEncryptionSanityChecks(c *C, errMatch string) { @@ -797,3 +802,278 @@ c.Check(installSystem.Err(), IsNil) c.Check(s.restartRequests, HasLen, 1) } + +func (s *deviceMgrInstallModeSuite) TestInstallCheckEncrypted(c *C) { + st := s.state + st.Lock() + defer st.Unlock() + + mockModel := s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ + "architecture": "amd64", + "kernel": "pc-kernel", + "gadget": "pc", + }) + devicestatetest.SetDevice(s.state, &auth.DeviceState{ + Brand: "canonical", + Model: "pc", + }) + deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel} + + for _, tc := range []struct { + hasFDESetupHook bool + hasTPM bool + encrypt bool + }{ + // unhappy: no tpm, no hook + {false, false, false}, + // happy: either tpm or hook or both + {false, true, true}, + {true, false, true}, + {true, true, true}, + } { + hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { + ctx.Lock() + defer ctx.Unlock() + ctx.Set("fde-setup-result", []byte(`{"features":[]}`)) + return nil, nil + } + rhk := hookstate.MockRunHook(hookInvoke) + defer rhk() + + if tc.hasFDESetupHook { + makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup) + } else { + makeInstalledMockKernelSnap(c, st, kernelYamlNoFdeSetup) + } + restore := devicestate.MockSecbootCheckKeySealingSupported(func() error { + if tc.hasTPM { + return nil + } + return fmt.Errorf("tpm says no") + }) + defer restore() + + encrypt, err := devicestate.DeviceManagerCheckEncryption(s.mgr, st, deviceCtx) + c.Assert(err, IsNil) + c.Check(encrypt, Equals, tc.encrypt, Commentf("%v", tc)) + } +} + +func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedStorageSafety(c *C) { + s.state.Lock() + defer s.state.Unlock() + + restore := devicestate.MockSecbootCheckKeySealingSupported(func() error { return nil }) + defer restore() + + var testCases = []struct { + grade, storageSafety string + + expectedEncryption bool + }{ + // we don't test unset here because the assertion assembly + // will ensure it has a default + {"dangerous", "prefer-unencrypted", false}, + {"dangerous", "prefer-encrypted", true}, + {"dangerous", "encrypted", true}, + {"signed", "prefer-unencrypted", false}, + {"signed", "prefer-encrypted", true}, + {"signed", "encrypted", true}, + // secured+prefer-{,un}encrypted is an error at the + // assertion level already so cannot be tested here + {"secured", "encrypted", true}, + } + for _, tc := range testCases { + mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{ + "display-name": "my model", + "architecture": "amd64", + "base": "core20", + "grade": tc.grade, + "storage-safety": tc.storageSafety, + "snaps": []interface{}{ + map[string]interface{}{ + "name": "pc-kernel", + "id": pcKernelSnapID, + "type": "kernel", + "default-channel": "20", + }, + map[string]interface{}{ + "name": "pc", + "id": pcSnapID, + "type": "gadget", + "default-channel": "20", + }}, + }) + deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel} + + encrypt, err := devicestate.DeviceManagerCheckEncryption(s.mgr, s.state, deviceCtx) + c.Assert(err, IsNil) + c.Check(encrypt, Equals, tc.expectedEncryption) + } +} + +func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedErrors(c *C) { + s.state.Lock() + defer s.state.Unlock() + + restore := devicestate.MockSecbootCheckKeySealingSupported(func() error { return fmt.Errorf("tpm says no") }) + defer restore() + + var testCases = []struct { + grade, storageSafety string + + expectedErr string + }{ + // we don't test unset here because the assertion assembly + // will ensure it has a default + { + "dangerous", "encrypted", + "cannot encrypt device storage as mandated by encrypted storage-safety model option: tpm says no", + }, { + "signed", "encrypted", + "cannot encrypt device storage as mandated by encrypted storage-safety model option: tpm says no", + }, { + "secured", "", + "cannot encrypt device storage as mandated by model grade secured: tpm says no", + }, { + "secured", "encrypted", + "cannot encrypt device storage as mandated by model grade secured: tpm says no", + }, + } + for _, tc := range testCases { + mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{ + "display-name": "my model", + "architecture": "amd64", + "base": "core20", + "grade": tc.grade, + "storage-safety": tc.storageSafety, + "snaps": []interface{}{ + map[string]interface{}{ + "name": "pc-kernel", + "id": pcKernelSnapID, + "type": "kernel", + "default-channel": "20", + }, + map[string]interface{}{ + "name": "pc", + "id": pcSnapID, + "type": "gadget", + "default-channel": "20", + }}, + }) + deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel} + _, err := devicestate.DeviceManagerCheckEncryption(s.mgr, s.state, deviceCtx) + c.Check(err, ErrorMatches, tc.expectedErr, Commentf("%s %s", tc.grade, tc.storageSafety)) + } +} + +func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedFDEHook(c *C) { + st := s.state + st.Lock() + defer st.Unlock() + + s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ + "architecture": "amd64", + "kernel": "pc-kernel", + "gadget": "pc", + }) + devicestatetest.SetDevice(s.state, &auth.DeviceState{ + Brand: "canonical", + Model: "pc", + }) + makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup) + + for _, tc := range []struct { + hookOutput string + expectedErr string + }{ + // invalid json + {"xxx", `cannot parse hook output "xxx": invalid character 'x' looking for beginning of value`}, + // no output is invalid + {"", `cannot parse hook output "": unexpected end of JSON input`}, + // specific error + {`{"error":"failed"}`, `cannot use hook: it returned error: failed`}, + {`{}`, `cannot use hook: neither "features" nor "error" returned`}, + // valid + {`{"features":[]}`, ""}, + {`{"features":["a"]}`, ""}, + {`{"features":["a","b"]}`, ""}, + // features must be list of strings + {`{"features":[1]}`, `cannot parse hook output ".*": json: cannot unmarshal number into Go struct.*`}, + {`{"features":1}`, `cannot parse hook output ".*": json: cannot unmarshal number into Go struct.*`}, + {`{"features":"1"}`, `cannot parse hook output ".*": json: cannot unmarshal string into Go struct.*`}, + } { + hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { + ctx.Lock() + defer ctx.Unlock() + ctx.Set("fde-setup-result", []byte(tc.hookOutput)) + return nil, nil + } + rhk := hookstate.MockRunHook(hookInvoke) + defer rhk() + + err := devicestate.DeviceManagerCheckFDEFeatures(s.mgr, st) + if tc.expectedErr != "" { + c.Check(err, ErrorMatches, tc.expectedErr, Commentf("%v", tc)) + } else { + c.Check(err, IsNil, Commentf("%v", tc)) + } + } +} + +var checkEncryptionModelHeaders = map[string]interface{}{ + "display-name": "my model", + "architecture": "amd64", + "base": "core20", + "grade": "dangerous", + "snaps": []interface{}{ + map[string]interface{}{ + "name": "pc-kernel", + "id": pcKernelSnapID, + "type": "kernel", + "default-channel": "20", + }, + map[string]interface{}{ + "name": "pc", + "id": pcSnapID, + "type": "gadget", + "default-channel": "20", + }}, +} + +func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedErrorsLogsTPM(c *C) { + s.state.Lock() + defer s.state.Unlock() + + restore := devicestate.MockSecbootCheckKeySealingSupported(func() error { + return fmt.Errorf("tpm says no") + }) + defer restore() + + logbuf, restore := logger.MockLogger() + defer restore() + + mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", checkEncryptionModelHeaders) + deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel} + _, err := devicestate.DeviceManagerCheckEncryption(s.mgr, s.state, deviceCtx) + c.Check(err, IsNil) + c.Check(logbuf.String(), Matches, "(?s).*: not encrypting device storage as checking TPM gave: tpm says no\n") +} + +func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedErrorsLogsHook(c *C) { + s.state.Lock() + defer s.state.Unlock() + + logbuf, restore := logger.MockLogger() + defer restore() + + mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", checkEncryptionModelHeaders) + // mock kernel installed but no hook or handle so checkEncryption + // will fail + makeInstalledMockKernelSnap(c, s.state, kernelYamlWithFdeSetup) + + deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel} + _, err := devicestate.DeviceManagerCheckEncryption(s.mgr, s.state, deviceCtx) + c.Check(err, IsNil) + c.Check(logbuf.String(), Matches, "(?s).*: not encrypting device storage as querying kernel fde-setup hook did not succeed:.*\n") +} diff -Nru snapd-2.48+20.04/overlord/devicestate/devicestate_serial_test.go snapd-2.48.3+20.04/overlord/devicestate/devicestate_serial_test.go --- snapd-2.48+20.04/overlord/devicestate/devicestate_serial_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/devicestate_serial_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -1998,6 +1998,13 @@ // as well savedb, err := sysdb.OpenAt(dirs.SnapDeviceSaveDir) c.Assert(err, IsNil) + // a copy of model was saved there + _, err = savedb.Find(asserts.ModelType, map[string]string{ + "series": "16", + "brand-id": "canonical", + "model": "pc-20", + }) + c.Assert(err, IsNil) // a copy of serial was backed up there _, err = savedb.Find(asserts.SerialType, map[string]string{ "brand-id": "canonical", diff -Nru snapd-2.48+20.04/overlord/devicestate/devicestate_test.go snapd-2.48.3+20.04/overlord/devicestate/devicestate_test.go --- snapd-2.48+20.04/overlord/devicestate/devicestate_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/devicestate_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -46,6 +46,7 @@ "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/devicestate" "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" + "github.com/snapcore/snapd/overlord/devicestate/fde" "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo" "github.com/snapcore/snapd/overlord/snapstate" @@ -53,6 +54,7 @@ "github.com/snapcore/snapd/overlord/state" "github.com/snapcore/snapd/overlord/storecontext" "github.com/snapcore/snapd/release" + "github.com/snapcore/snapd/secboot" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" "github.com/snapcore/snapd/snapdenv" @@ -1315,6 +1317,154 @@ } } +func (s *deviceMgrSuite) TestRunFdeSetupHookOpInitialSetup(c *C) { + st := s.state + + st.Lock() + makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup) + mockModel := s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ + "architecture": "amd64", + "kernel": "pc-kernel", + "gadget": "pc", + }) + devicestatetest.SetDevice(s.state, &auth.DeviceState{ + Brand: "canonical", + Model: "pc", + }) + st.Unlock() + + mockKey := secboot.EncryptionKey{1, 2, 3, 4} + + var hookCalled []string + hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { + ctx.Lock() + defer ctx.Unlock() + + // check that the context has the right data + c.Check(ctx.HookName(), Equals, "fde-setup") + var fdeSetup fde.SetupRequest + ctx.Get("fde-setup-request", &fdeSetup) + c.Check(fdeSetup, DeepEquals, fde.SetupRequest{ + Op: "initial-setup", + Key: &mockKey, + KeyName: "some-key-name", + Models: []map[string]string{ + { + "series": "16", + "brand-id": "canonical", + "model": "pc", + "grade": "unset", + "signkey-id": mockModel.SignKeyID(), + }, + }, + }) + + // the snapctl fde-setup-result will set the data + ctx.Set("fde-setup-result", []byte("sealed-key")) + hookCalled = append(hookCalled, ctx.InstanceName()) + return nil, nil + } + + rhk := hookstate.MockRunHook(hookInvoke) + defer rhk() + + s.o.Loop() + defer s.o.Stop() + + params := &boot.FDESetupHookParams{ + Key: mockKey, + KeyName: "some-key-name", + Models: []*asserts.Model{mockModel}, + } + st.Lock() + data, err := devicestate.DeviceManagerRunFDESetupHook(s.mgr, "initial-setup", params) + st.Unlock() + c.Assert(err, IsNil) + c.Check(string(data), Equals, "sealed-key") + c.Check(hookCalled, DeepEquals, []string{"pc-kernel"}) +} + +func (s *deviceMgrSuite) TestRunFdeSetupHookOpInitialSetupErrors(c *C) { + st := s.state + + st.Lock() + makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup) + mockModel := s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ + "architecture": "amd64", + "kernel": "pc-kernel", + "gadget": "pc", + }) + devicestatetest.SetDevice(s.state, &auth.DeviceState{ + Brand: "canonical", + Model: "pc", + }) + st.Unlock() + + hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { + return nil, fmt.Errorf("hook failed") + } + + rhk := hookstate.MockRunHook(hookInvoke) + defer rhk() + + s.o.Loop() + defer s.o.Stop() + + params := &boot.FDESetupHookParams{ + Key: secboot.EncryptionKey{1, 2, 3, 4}, + KeyName: "some-key-name", + Models: []*asserts.Model{mockModel}, + } + st.Lock() + _, err := devicestate.DeviceManagerRunFDESetupHook(s.mgr, "initial-setup", params) + st.Unlock() + c.Assert(err, ErrorMatches, `cannot run hook for "initial-setup": run hook "fde-setup": hook failed`) +} + +func (s *deviceMgrSuite) TestRunFdeSetupHookOpInitialSetupErrorResult(c *C) { + st := s.state + + st.Lock() + makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup) + mockModel := s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ + "architecture": "amd64", + "kernel": "pc-kernel", + "gadget": "pc", + }) + devicestatetest.SetDevice(s.state, &auth.DeviceState{ + Brand: "canonical", + Model: "pc", + }) + st.Unlock() + + hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { + // Simulate an incorrect type for fde-setup-result here to + // test error string from runFDESetupHook. + // This should never happen in practice, "snapctl fde-setup" + // will always set this to []byte + ctx.Lock() + ctx.Set("fde-setup-result", "not-bytes") + ctx.Unlock() + return nil, nil + } + + rhk := hookstate.MockRunHook(hookInvoke) + defer rhk() + + s.o.Loop() + defer s.o.Stop() + + params := &boot.FDESetupHookParams{ + Key: secboot.EncryptionKey{1, 2, 3, 4}, + KeyName: "some-key-name", + Models: []*asserts.Model{mockModel}, + } + st.Lock() + _, err := devicestate.DeviceManagerRunFDESetupHook(s.mgr, "initial-setup", params) + st.Unlock() + c.Assert(err, ErrorMatches, `cannot get result from fde-setup hook "initial-setup": cannot unmarshal context value for "fde-setup-result": illegal base64 data at input byte 3`) +} + type startOfOperationTimeSuite struct { state *state.State mgr *devicestate.DeviceManager diff -Nru snapd-2.48+20.04/overlord/devicestate/export_test.go snapd-2.48.3+20.04/overlord/devicestate/export_test.go --- snapd-2.48+20.04/overlord/devicestate/export_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/export_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -291,3 +291,15 @@ func DeviceManagerHasFDESetupHook(mgr *DeviceManager) (bool, error) { return mgr.hasFDESetupHook() } + +func DeviceManagerRunFDESetupHook(mgr *DeviceManager, op string, params *boot.FDESetupHookParams) ([]byte, error) { + return mgr.runFDESetupHook(op, params) +} + +func DeviceManagerCheckEncryption(mgr *DeviceManager, st *state.State, deviceCtx snapstate.DeviceContext) (bool, error) { + return mgr.checkEncryption(st, deviceCtx) +} + +func DeviceManagerCheckFDEFeatures(mgr *DeviceManager, st *state.State) error { + return mgr.checkFDEFeatures(st) +} diff -Nru snapd-2.48+20.04/overlord/devicestate/fde/fde.go snapd-2.48.3+20.04/overlord/devicestate/fde/fde.go --- snapd-2.48+20.04/overlord/devicestate/fde/fde.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/fde/fde.go 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,64 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2020 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// package fde implements helper used by low level parts like secboot +// in snap-bootstrap and high level parts like DeviceManager in snapd. +// +// Note that it must never import anything overlord related itself +// to avoid increasing the size of snap-bootstrap. +package fde + +import ( + "os/exec" + + "github.com/snapcore/snapd/secboot" +) + +func init() { + secboot.FDEHasRevealKey = HasRevealKey +} + +// HasRevealKey return true if the current system has a "fde-reveal-key" +// binary (usually used in the initrd). +// +// This will be setup by devicestate to support device-specific full +// disk encryption implementations. +func HasRevealKey() bool { + // XXX: should we record during initial sealing that the fde-setup + // was used and only use fde-reveal-key in that case? + _, err := exec.LookPath("fde-reveal-key") + return err == nil +} + +// SetupRequest carries the operation and parameters for the fde-setup hooks +// made available to them via the snapctl fde-setup-request command. +type SetupRequest struct { + // XXX: make "op" a type: "features", "initial-setup", "update" ? + Op string `json:"op"` + + Key *secboot.EncryptionKey `json:"key,omitempty"` + KeyName string `json:"key-name,omitempty"` + + // List of models with their related fields, this will be set + // to follow the secboot:SnapModel interface. + Models []map[string]string `json:"models,omitempty"` + + // TODO: provide LoadChains, KernelCmdline etc to support full + // tpm sealing +} diff -Nru snapd-2.48+20.04/overlord/devicestate/fde/fde_test.go snapd-2.48.3+20.04/overlord/devicestate/fde/fde_test.go --- snapd-2.48+20.04/overlord/devicestate/fde/fde_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/fde/fde_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,59 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2020 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package fde_test + +import ( + "io/ioutil" + "os" + "testing" + + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/overlord/devicestate/fde" +) + +func TestFde(t *testing.T) { TestingT(t) } + +type fdeSuite struct{} + +var _ = Suite(&fdeSuite{}) + +func (s *fdeSuite) TestHasRevealKey(c *C) { + oldPath := os.Getenv("PATH") + defer func() { os.Setenv("PATH", oldPath) }() + + mockRoot := c.MkDir() + os.Setenv("PATH", mockRoot+"/bin") + mockBin := mockRoot + "/bin/" + err := os.Mkdir(mockBin, 0755) + c.Assert(err, IsNil) + + // no fde-reveal-key binary + c.Check(fde.HasRevealKey(), Equals, false) + + // fde-reveal-key without +x + err = ioutil.WriteFile(mockBin+"fde-reveal-key", nil, 0644) + c.Assert(err, IsNil) + c.Check(fde.HasRevealKey(), Equals, false) + + // correct fde-reveal-key, no logging + err = os.Chmod(mockBin+"fde-reveal-key", 0755) + c.Assert(err, IsNil) +} diff -Nru snapd-2.48+20.04/overlord/devicestate/handlers_install.go snapd-2.48.3+20.04/overlord/devicestate/handlers_install.go --- snapd-2.48+20.04/overlord/devicestate/handlers_install.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/handlers_install.go 2021-02-02 08:21:12.000000000 +0000 @@ -126,7 +126,7 @@ bopts := install.Options{ Mount: true, } - useEncryption, err := checkEncryption(deviceCtx.Model()) + useEncryption, err := m.checkEncryption(st, deviceCtx) if err != nil { return err } @@ -301,10 +301,13 @@ var secbootCheckKeySealingSupported = secboot.CheckKeySealingSupported // checkEncryption verifies whether encryption should be used based on the -// model grade and the availability of a TPM device. -func checkEncryption(model *asserts.Model) (res bool, err error) { +// model grade and the availability of a TPM device or a fde-setup hook +// in the kernel. +func (m *DeviceManager) checkEncryption(st *state.State, deviceCtx snapstate.DeviceContext) (res bool, err error) { + model := deviceCtx.Model() secured := model.Grade() == asserts.ModelSecured dangerous := model.Grade() == asserts.ModelDangerous + encrypted := model.StorageSafety() == asserts.StorageSafetyEncrypted // check if we should disable encryption non-secured devices // TODO:UC20: this is not the final mechanism to bypass encryption @@ -312,13 +315,49 @@ return false, nil } - // encryption is required in secured devices and optional in other grades - if err := secbootCheckKeySealingSupported(); err != nil { + // check if the model prefers to be unencrypted + // TODO: provide way to select via install chooser menu + // if the install is unencrypted or encrypted + if model.StorageSafety() == asserts.StorageSafetyPreferUnencrypted { + logger.Noticef(`installing system unencrypted to comply with prefer-unencrypted storage-safety model option`) + return false, nil + } + + // check if encryption is available + var ( + hasFDESetupHook bool + checkEncryptionErr error + ) + if kernelInfo, err := snapstate.KernelInfo(st, deviceCtx); err == nil { + if hasFDESetupHook = hasFDESetupHookInKernel(kernelInfo); hasFDESetupHook { + checkEncryptionErr = m.checkFDEFeatures(st) + } + } + // Note that having a fde-setup hook will disable the build-in + // secboot encryption + if !hasFDESetupHook { + checkEncryptionErr = secbootCheckKeySealingSupported() + } + + // check if encryption is required + if checkEncryptionErr != nil { if secured { - return false, fmt.Errorf("cannot encrypt secured device: %v", err) + return false, fmt.Errorf("cannot encrypt device storage as mandated by model grade secured: %v", checkEncryptionErr) } + if encrypted { + return false, fmt.Errorf("cannot encrypt device storage as mandated by encrypted storage-safety model option: %v", checkEncryptionErr) + } + + if hasFDESetupHook { + logger.Noticef("not encrypting device storage as querying kernel fde-setup hook did not succeed: %v", checkEncryptionErr) + } else { + logger.Noticef("not encrypting device storage as checking TPM gave: %v", checkEncryptionErr) + } + + // not required, go without return false, nil } + // encrypt return true, nil } diff -Nru snapd-2.48+20.04/overlord/devicestate/handlers_serial.go snapd-2.48.3+20.04/overlord/devicestate/handlers_serial.go --- snapd-2.48+20.04/overlord/devicestate/handlers_serial.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/devicestate/handlers_serial.go 2021-02-02 08:21:12.000000000 +0000 @@ -694,6 +694,12 @@ } b := asserts.NewBatch(nil) err := b.Fetch(savedb, retrieve, func(f asserts.Fetcher) error { + // save the associated model as well + // as it might be required for cross-checks + // of the serial + if err := f.Save(regCtx.Model()); err != nil { + return err + } return f.Save(serial) }) if err != nil { diff -Nru snapd-2.48+20.04/overlord/hookstate/context.go snapd-2.48.3+20.04/overlord/hookstate/context.go --- snapd-2.48+20.04/overlord/hookstate/context.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/hookstate/context.go 2021-02-02 08:21:12.000000000 +0000 @@ -137,6 +137,28 @@ } } +func (c *Context) initForRun(handler Handler, contextData map[string]interface{}) error { + c.handler = handler + if contextData == nil { + return nil + } + + if !c.IsEphemeral() { + return fmt.Errorf("internal error: cannot pass contextData to initForRun for %v", c) + } + + serialized, err := json.Marshal(contextData) + if err != nil { + return err + } + var data map[string]*json.RawMessage + if err := json.Unmarshal(serialized, &data); err != nil { + return err + } + c.cache["ephemeral-context"] = data + return nil +} + // Set associates value with key. The provided value must properly marshal and // unmarshal with encoding/json. Note that the context needs to be locked and // unlocked by the caller. diff -Nru snapd-2.48+20.04/overlord/hookstate/ctlcmd/fde_setup.go snapd-2.48.3+20.04/overlord/hookstate/ctlcmd/fde_setup.go --- snapd-2.48+20.04/overlord/hookstate/ctlcmd/fde_setup.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/hookstate/ctlcmd/fde_setup.go 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,139 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2020 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package ctlcmd + +import ( + "encoding/json" + "fmt" + + "github.com/snapcore/snapd/i18n" + "github.com/snapcore/snapd/overlord/devicestate/fde" +) + +type fdeSetupRequestCommand struct { + baseCommand +} + +var shortFdeSetupRequestHelp = i18n.G("Obtain full disk encryption setup request") + +var longFdeSetupRequestHelp = i18n.G(` +The fde-setup-request command is used inside the fde-setup hook. It will +return information about what operation for full-disk encryption is +requested and auxiliary data to complete this operation. + +The fde-setup hook should do what is requested and then call +"snapctl fde-setup-result" and pass the result data to stdin. + +Here is an example for how the fde-setup hook is called initially: +$ snapctl fde-setup-request +{"op":"features"} +$ echo '[]' | snapctl fde-setup-result + +Alternatively the hook could reply with: +$ echo '{"error":"hardware-unsupported"}' | snapctl fde-setup-result + +And then it is called again with a request to do the initial key setup: +$ snapctl fde-setup-request +{"op":"initial-setup", "key": "key-to-seal", "key-name":"key-for-ubuntu-data"} +$ echo "$sealed_key" | snapctl fde-setup-result +`) + +func init() { + addCommand("fde-setup-request", shortFdeSetupRequestHelp, longFdeSetupRequestHelp, func() command { return &fdeSetupRequestCommand{} }) +} + +func (c *fdeSetupRequestCommand) Execute(args []string) error { + context := c.context() + if context == nil { + return fmt.Errorf("cannot run fde-setup-request without a context") + } + context.Lock() + defer context.Unlock() + + if context.HookName() != "fde-setup" { + return fmt.Errorf("cannot use fde-setup-request outside of the fde-setup hook") + } + + var fdeSetup fde.SetupRequest + if err := context.Get("fde-setup-request", &fdeSetup); err != nil { + return fmt.Errorf("cannot get fde-setup-op from context: %v", err) + } + // Op is either "initial-setup" or "features" + switch fdeSetup.Op { + case "features", "initial-setup": + // fine + default: + return fmt.Errorf("unknown fde-setup-request op %q", fdeSetup.Op) + + } + + bytes, err := json.Marshal(fdeSetup) + if err != nil { + return fmt.Errorf("cannot json print fde key: %v", err) + } + c.printf("%s\n", string(bytes)) + + return nil +} + +type fdeSetupResultCommand struct { + baseCommand +} + +var shortFdeSetupResultHelp = i18n.G("Set result for full disk encryption") +var longFdeSetupResultHelp = i18n.G(` +The fde-setup-result command sets the result data for a fde-setup hook +reading it from stdin. + +For example: +When the fde-setup hook is called with "op":"features: +$ echo "[]" | snapctl fde-setup-result + +When the fde-setup hook is called with "op":"initial-setup": +$ echo "sealed-key" | snapctl fde-setup-result +`) + +func init() { + addCommand("fde-setup-result", shortFdeSetupResultHelp, longFdeSetupResultHelp, func() command { return &fdeSetupResultCommand{} }) +} + +func (c *fdeSetupResultCommand) Execute(args []string) error { + context := c.context() + if context == nil { + return fmt.Errorf("cannot run fde-setup-result without a context") + } + context.Lock() + defer context.Unlock() + + if context.HookName() != "fde-setup" { + return fmt.Errorf("cannot use fde-setup-result outside of the fde-setup hook") + } + + var fdeSetupResult []byte + if err := context.Get("stdin", &fdeSetupResult); err != nil { + return fmt.Errorf("internal error: cannot get result from stdin: %v", err) + } + if fdeSetupResult == nil { + return fmt.Errorf("no result data found from stdin") + } + context.Set("fde-setup-result", fdeSetupResult) + + return nil +} diff -Nru snapd-2.48+20.04/overlord/hookstate/ctlcmd/fde_setup_test.go snapd-2.48.3+20.04/overlord/hookstate/ctlcmd/fde_setup_test.go --- snapd-2.48+20.04/overlord/hookstate/ctlcmd/fde_setup_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/hookstate/ctlcmd/fde_setup_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,174 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2020 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package ctlcmd_test + +import ( + "fmt" + "strings" + + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/overlord/devicestate/fde" + "github.com/snapcore/snapd/overlord/hookstate" + "github.com/snapcore/snapd/overlord/hookstate/ctlcmd" + "github.com/snapcore/snapd/overlord/hookstate/hooktest" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/secboot" + "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/testutil" +) + +type fdeSetupSuite struct { + testutil.BaseTest + + st *state.State + mockHandler *hooktest.MockHandler + mockTask *state.Task + mockContext *hookstate.Context +} + +var _ = Suite(&fdeSetupSuite{}) + +var mockFdeSetupKernelYaml = `name: pc-kernel +version: 1.0 +type: kernel +hooks: + fde-setup: +` + +func (s *fdeSetupSuite) SetUpTest(c *C) { + s.BaseTest.SetUpTest(c) + dirs.SetRootDir(c.MkDir()) + s.AddCleanup(func() { dirs.SetRootDir("/") }) + + s.st = state.New(nil) + s.mockHandler = hooktest.NewMockHandler() + s.st.Lock() + defer s.st.Unlock() + + mockInstalledSnap(c, s.st, mockFdeSetupKernelYaml) + s.mockTask = s.st.NewTask("test-task", "my test task") + hooksup := &hookstate.HookSetup{ + Snap: "pc-kernel", + Revision: snap.R(1), + Hook: "fde-setup", + } + context, err := hookstate.NewContext(s.mockTask, s.st, hooksup, s.mockHandler, "") + c.Assert(err, IsNil) + s.mockContext = context +} + +func (s *fdeSetupSuite) TestFdeSetupRequestOpInvalid(c *C) { + fdeSetup := &fde.SetupRequest{ + Op: "invalid-and-unknown", + } + s.mockContext.Lock() + s.mockContext.Set("fde-setup-request", fdeSetup) + s.mockContext.Unlock() + + stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"fde-setup-request"}, 0) + c.Check(err, ErrorMatches, `unknown fde-setup-request op "invalid-and-unknown"`) + c.Check(string(stdout), Equals, "") + c.Check(string(stderr), Equals, "") +} + +func (s *fdeSetupSuite) TestFdeSetupRequestNoFdeSetupOpData(c *C) { + hooksup := &hookstate.HookSetup{ + Snap: "pc-kernel", + Revision: snap.R(1), + Hook: "other-hook", + } + context, err := hookstate.NewContext(nil, s.st, hooksup, s.mockHandler, "") + c.Assert(err, IsNil) + + // check "fde-setup-request" error + stdout, stderr, err := ctlcmd.Run(context, []string{"fde-setup-request"}, 0) + c.Check(err, ErrorMatches, `cannot use fde-setup-request outside of the fde-setup hook`) + c.Check(string(stdout), Equals, "") + c.Check(string(stderr), Equals, "") + + // check "fde-setup-result" error + stdout, stderr, err = ctlcmd.Run(context, []string{"fde-setup-result"}, 0) + c.Check(err, ErrorMatches, `cannot use fde-setup-result outside of the fde-setup hook`) + c.Check(string(stdout), Equals, "") + c.Check(string(stderr), Equals, "") +} + +func (s *fdeSetupSuite) TestFdeSetupRequestOpFeatures(c *C) { + fdeSetup := &fde.SetupRequest{ + Op: "features", + } + s.mockContext.Lock() + s.mockContext.Set("fde-setup-request", fdeSetup) + s.mockContext.Unlock() + + stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"fde-setup-request"}, 0) + c.Assert(err, IsNil) + c.Check(string(stdout), Equals, `{"op":"features"}`+"\n") + c.Check(string(stderr), Equals, "") +} + +func (s *fdeSetupSuite) TestFdeSetupRequestOpInitialSetup(c *C) { + fdeSetup := &fde.SetupRequest{ + Op: "initial-setup", + Key: &secboot.EncryptionKey{1, 2, 3, 4}, + KeyName: "the-key-name", + Models: []map[string]string{ + { + "series": "16", + "brand-id": "my-brand", + "model": "my-model", + "grade": "secured", + "signkey-id": "the-signkey-id", + }, + }, + } + s.mockContext.Lock() + s.mockContext.Set("fde-setup-request", fdeSetup) + s.mockContext.Unlock() + + stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"fde-setup-request"}, 0) + c.Assert(err, IsNil) + + jsonEncodedEncryptionKey := `[1,2,3,4,` + strings.Repeat("0,", len(secboot.EncryptionKey{})-5) + `0]` + c.Check(string(stdout), Equals, fmt.Sprintf(`{"op":"initial-setup","key":%s,"key-name":"the-key-name","models":[{"brand-id":"my-brand","grade":"secured","model":"my-model","series":"16","signkey-id":"the-signkey-id"}]}`+"\n", jsonEncodedEncryptionKey)) + c.Check(string(stderr), Equals, "") +} + +func (s *fdeSetupSuite) TestFdeSetupResult(c *C) { + mockStdin := []byte("sealed-key-data-from-stdin-as-set-by-daemon:runSnapctl") + + s.mockContext.Lock() + s.mockContext.Set("stdin", mockStdin) + s.mockContext.Unlock() + + stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"fde-setup-result"}, 0) + c.Assert(err, IsNil) + c.Check(string(stdout), Equals, "") + c.Check(string(stderr), Equals, "") + + // check that the task got the key that was passed via stdin + var fdeSetupResult []byte + s.mockContext.Lock() + s.mockContext.Get("fde-setup-result", &fdeSetupResult) + s.mockContext.Unlock() + c.Check(fdeSetupResult, DeepEquals, mockStdin) +} diff -Nru snapd-2.48+20.04/overlord/hookstate/hookmgr.go snapd-2.48.3+20.04/overlord/hookstate/hookmgr.go --- snapd-2.48+20.04/overlord/hookstate/hookmgr.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/hookstate/hookmgr.go 2021-02-02 08:21:12.000000000 +0000 @@ -20,6 +20,7 @@ package hookstate import ( + "context" "fmt" "os" "path/filepath" @@ -260,7 +261,7 @@ return fmt.Errorf("cannot extract hook setup from task: %s", err) } - return m.runHook(task, tomb, snapst, hooksup) + return m.runHookForTask(task, tomb, snapst, hooksup) } // undoRunHook runs the undo-hook that was requested. @@ -279,34 +280,52 @@ return fmt.Errorf("cannot extract undo hook setup from task: %s", err) } - return m.runHook(task, tomb, snapst, hooksup) + return m.runHookForTask(task, tomb, snapst, hooksup) } -func (m *HookManager) runHook(task *state.Task, tomb *tomb.Tomb, snapst *snapstate.SnapState, hooksup *HookSetup) error { +func (m *HookManager) EphemeralRunHook(ctx context.Context, hooksup *HookSetup, contextData map[string]interface{}) (*Context, error) { + var snapst snapstate.SnapState + m.state.Lock() + err := snapstate.Get(m.state, hooksup.Snap, &snapst) + m.state.Unlock() + if err != nil { + return nil, fmt.Errorf("cannot run ephemeral hook %q for snap %q: %v", hooksup.Hook, hooksup.Snap, err) + } + + tomb, _ := tomb.WithContext(ctx) + return m.runHookCommon(nil, tomb, &snapst, hooksup, contextData) +} + +func (m *HookManager) runHookForTask(task *state.Task, tomb *tomb.Tomb, snapst *snapstate.SnapState, hooksup *HookSetup) error { + _, err := m.runHookCommon(task, tomb, snapst, hooksup, nil) + return err +} + +func (m *HookManager) runHookCommon(task *state.Task, tomb *tomb.Tomb, snapst *snapstate.SnapState, hooksup *HookSetup, contextData map[string]interface{}) (*Context, error) { mustHijack := m.hijacked(hooksup.Hook, hooksup.Snap) != nil hookExists := false if !mustHijack { // not hijacked, snap must be installed if !snapst.IsInstalled() { - return fmt.Errorf("cannot find %q snap", hooksup.Snap) + return nil, fmt.Errorf("cannot find %q snap", hooksup.Snap) } info, err := snapst.CurrentInfo() if err != nil { - return fmt.Errorf("cannot read %q snap details: %v", hooksup.Snap, err) + return nil, fmt.Errorf("cannot read %q snap details: %v", hooksup.Snap, err) } hookExists = info.Hooks[hooksup.Hook] != nil if !hookExists && !hooksup.Optional { - return fmt.Errorf("snap %q has no %q hook", hooksup.Snap, hooksup.Hook) + return nil, fmt.Errorf("snap %q has no %q hook", hooksup.Snap, hooksup.Hook) } } if hookExists || mustHijack { // we will run something, not a noop - if ok, _ := task.State().Restarting(); ok { + if ok, _ := m.state.Restarting(); ok { // don't start running a hook if we are restarting - return &state.Retry{} + return nil, &state.Retry{} } // keep count of running hooks @@ -314,12 +333,12 @@ defer atomic.AddInt32(&m.runningHooks, -1) } else if !hooksup.Always { // a noop with no 'always' flag: bail - return nil + return nil, nil } - context, err := NewContext(task, task.State(), hooksup, nil, "") + context, err := NewContext(task, m.state, hooksup, nil, "") if err != nil { - return err + return nil, err } // Obtain a handler for this hook. The repository returns a list since it's @@ -332,14 +351,16 @@ // This is to avoid issues when downgrading to an old core snap that doesn't know about // particular hook type and a task for it exists (e.g. "post-refresh" hook). if hooksup.Optional { - return nil + return nil, nil } - return fmt.Errorf("internal error: no registered handlers for hook %q", hooksup.Hook) + return nil, fmt.Errorf("internal error: no registered handlers for hook %q", hooksup.Hook) } if handlersCount > 1 { - return fmt.Errorf("internal error: %d handlers registered for hook %q, expected 1", handlersCount, hooksup.Hook) + return nil, fmt.Errorf("internal error: %d handlers registered for hook %q, expected 1", handlersCount, hooksup.Hook) + } + if err := context.initForRun(handlers[0], contextData); err != nil { + return nil, err } - context.handler = handlers[0] contextID := context.ID() m.contextsMutex.Lock() @@ -353,7 +374,7 @@ }() if err = context.Handler().Before(); err != nil { - return err + return nil, err } // some hooks get hijacked, e.g. the core configuration @@ -369,29 +390,31 @@ } err = osutil.OutputErr(output, err) if hooksup.IgnoreError { - task.State().Lock() - task.Errorf("ignoring failure in hook %q: %v", hooksup.Hook, err) - task.State().Unlock() + if task != nil { + task.State().Lock() + task.Errorf("ignoring failure in hook %q: %v", hooksup.Hook, err) + task.State().Unlock() + } } else { if handlerErr := context.Handler().Error(err); handlerErr != nil { - return handlerErr + return nil, handlerErr } - return fmt.Errorf("run hook %q: %v", hooksup.Hook, err) + return nil, fmt.Errorf("run hook %q: %v", hooksup.Hook, err) } } if err = context.Handler().Done(); err != nil { - return err + return nil, err } context.Lock() defer context.Unlock() if err = context.Done(); err != nil { - return err + return nil, err } - return nil + return context, nil } func runHookImpl(c *Context, tomb *tomb.Tomb) ([]byte, error) { diff -Nru snapd-2.48+20.04/overlord/hookstate/hookstate_test.go snapd-2.48.3+20.04/overlord/hookstate/hookstate_test.go --- snapd-2.48+20.04/overlord/hookstate/hookstate_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/overlord/hookstate/hookstate_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -20,6 +20,7 @@ package hookstate_test import ( + "context" "encoding/json" "fmt" "path/filepath" @@ -29,6 +30,7 @@ "time" . "gopkg.in/check.v1" + "gopkg.in/tomb.v2" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/overlord" @@ -1173,6 +1175,110 @@ c.Assert(err, IsNil) } +func (s *hookManagerSuite) TestEphemeralRunHook(c *C) { + contextData := map[string]interface{}{ + "key": "value", + "key2": "value2", + } + s.testEphemeralRunHook(c, contextData) +} + +func (s *hookManagerSuite) TestEphemeralRunHookNoContextData(c *C) { + var contextData map[string]interface{} = nil + s.testEphemeralRunHook(c, contextData) +} + +func (s *hookManagerSuite) testEphemeralRunHook(c *C, contextData map[string]interface{}) { + var hookInvokeCalled []string + hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { + c.Check(ctx.HookName(), Equals, "configure") + hookInvokeCalled = append(hookInvokeCalled, ctx.HookName()) + + // check that context data was set correctly + var s string + ctx.Lock() + defer ctx.Unlock() + for k, v := range contextData { + ctx.Get(k, &s) + c.Check(s, Equals, v) + } + ctx.Set("key-set-from-hook", "value-set-from-hook") + + return []byte("some output"), nil + } + restore := hookstate.MockRunHook(hookInvoke) + defer restore() + + hooksup := &hookstate.HookSetup{ + Snap: "test-snap", + Revision: snap.R(1), + Hook: "configure", + } + context, err := s.manager.EphemeralRunHook(context.Background(), hooksup, contextData) + c.Assert(err, IsNil) + c.Check(hookInvokeCalled, DeepEquals, []string{"configure"}) + + var value string + context.Lock() + context.Get("key-set-from-hook", &value) + context.Unlock() + c.Check(value, Equals, "value-set-from-hook") +} + +func (s *hookManagerSuite) TestEphemeralRunHookNoSnap(c *C) { + hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { + c.Fatalf("hook should not be invoked in this test") + return nil, nil + } + restore := hookstate.MockRunHook(hookInvoke) + defer restore() + + hooksup := &hookstate.HookSetup{ + Snap: "not-installed-snap", + Revision: snap.R(1), + Hook: "configure", + } + contextData := map[string]interface{}{ + "key": "value", + } + _, err := s.manager.EphemeralRunHook(context.Background(), hooksup, contextData) + c.Assert(err, ErrorMatches, `cannot run ephemeral hook "configure" for snap "not-installed-snap": no state entry for key`) +} + +func (s *hookManagerSuite) TestEphemeralRunHookContextCanCancel(c *C) { + tombDying := 0 + hookRunning := make(chan struct{}) + + hookInvoke := func(_ *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { + close(hookRunning) + + select { + case <-tomb.Dying(): + tombDying++ + case <-time.After(10 * time.Second): + c.Fatalf("hook not canceled after 10s") + } + return nil, nil + } + restore := hookstate.MockRunHook(hookInvoke) + defer restore() + + hooksup := &hookstate.HookSetup{ + Snap: "test-snap", + Revision: snap.R(1), + Hook: "configure", + } + + ctx, cancelFunc := context.WithCancel(context.Background()) + go func() { + <-hookRunning + cancelFunc() + }() + _, err := s.manager.EphemeralRunHook(ctx, hooksup, nil) + c.Assert(err, IsNil) + c.Check(tombDying, Equals, 1) +} + type parallelInstancesHookManagerSuite struct { baseHookManagerSuite } diff -Nru snapd-2.48+20.04/packaging/amzn-2/snapd.spec snapd-2.48.3+20.04/packaging/amzn-2/snapd.spec --- snapd-2.48+20.04/packaging/amzn-2/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/amzn-2/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/arch/PKGBUILD snapd-2.48.3+20.04/packaging/arch/PKGBUILD --- snapd-2.48+20.04/packaging/arch/PKGBUILD 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/arch/PKGBUILD 2021-02-02 08:21:12.000000000 +0000 @@ -11,7 +11,7 @@ depends=('squashfs-tools' 'libseccomp' 'libsystemd' 'apparmor') optdepends=('bash-completion: bash completion support' 'xdg-desktop-portal: desktop integration') -pkgver=2.48 +pkgver=2.48.3 pkgrel=1 arch=('x86_64' 'i686' 'armv7h' 'aarch64') url="https://github.com/snapcore/snapd" diff -Nru snapd-2.48+20.04/packaging/centos-7/snapd.spec snapd-2.48.3+20.04/packaging/centos-7/snapd.spec --- snapd-2.48+20.04/packaging/centos-7/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/centos-7/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/centos-8/snapd.spec snapd-2.48.3+20.04/packaging/centos-8/snapd.spec --- snapd-2.48+20.04/packaging/centos-8/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/centos-8/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/debian-sid/changelog snapd-2.48.3+20.04/packaging/debian-sid/changelog --- snapd-2.48+20.04/packaging/debian-sid/changelog 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/debian-sid/changelog 2021-02-02 08:21:12.000000000 +0000 @@ -1,3 +1,68 @@ +snapd (2.48.3-1) unstable; urgency=medium + + * SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + * interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + + -- Michael Vogt Tue, 02 Feb 2021 09:21:12 +0100 + +snapd (2.48.2-1) unstable; urgency=medium + + * New upstream release, LP: #1906690 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + + -- Michael Vogt Tue, 15 Dec 2020 20:21:44 +0100 + +snapd (2.48.1-1) unstable; urgency=medium + + * New upstream release, LP: #1906690 + - gadget: disable ubuntu-boot role validation check + + -- Michael Vogt Thu, 03 Dec 2020 17:43:30 +0100 + snapd (2.48-1) unstable; urgency=medium * New upstream release, LP: #1904098 diff -Nru snapd-2.48+20.04/packaging/fedora/snapd.spec snapd-2.48.3+20.04/packaging/fedora/snapd.spec --- snapd-2.48+20.04/packaging/fedora/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/fedora/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/fedora-29/snapd.spec snapd-2.48.3+20.04/packaging/fedora-29/snapd.spec --- snapd-2.48+20.04/packaging/fedora-29/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/fedora-29/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/fedora-30/snapd.spec snapd-2.48.3+20.04/packaging/fedora-30/snapd.spec --- snapd-2.48+20.04/packaging/fedora-30/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/fedora-30/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/fedora-31/snapd.spec snapd-2.48.3+20.04/packaging/fedora-31/snapd.spec --- snapd-2.48+20.04/packaging/fedora-31/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/fedora-31/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/fedora-32/snapd.spec snapd-2.48.3+20.04/packaging/fedora-32/snapd.spec --- snapd-2.48+20.04/packaging/fedora-32/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/fedora-32/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/fedora-rawhide/snapd.spec snapd-2.48.3+20.04/packaging/fedora-rawhide/snapd.spec --- snapd-2.48+20.04/packaging/fedora-rawhide/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/fedora-rawhide/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -97,7 +97,7 @@ %endif Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0%{?dist} Summary: A transactional software package manager License: GPLv3 @@ -915,6 +915,63 @@ %changelog +* Tue Feb 02 2021 Michael Vogt +- New upstream release 2.48.3 + - SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + - interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + +* Thu Dec 15 2020 Michael Vogt +- New upstream release 2.48.2 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + +* Thu Dec 03 2020 Michael Vogt +- New upstream release 2.48.1 + - gadget: disable ubuntu-boot role validation check + * Thu Nov 19 2020 Michael Vogt - New upstream release 2.48 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/packaging/opensuse/snapd.changes snapd-2.48.3+20.04/packaging/opensuse/snapd.changes --- snapd-2.48+20.04/packaging/opensuse/snapd.changes 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse/snapd.changes 2021-02-02 08:21:12.000000000 +0000 @@ -1,4 +1,19 @@ ------------------------------------------------------------------- +Tue, 02 Feb 2021 09:24:52 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.3 + +------------------------------------------------------------------- +Tue, 15 Dec 2020 20:38:36 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.2 + +------------------------------------------------------------------- +Thu, 03 Dec 2020 17:46:49 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.1 + +------------------------------------------------------------------- Thu, 19 Nov 2020 17:55:24 +0100 - mvo@ubuntu.com - Update to upstream release 2.48 diff -Nru snapd-2.48+20.04/packaging/opensuse/snapd.spec snapd-2.48.3+20.04/packaging/opensuse/snapd.spec --- snapd-2.48+20.04/packaging/opensuse/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -83,7 +83,7 @@ Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0 Summary: Tools enabling systems to work with .snap files License: GPL-3.0 diff -Nru snapd-2.48+20.04/packaging/opensuse-15.0/snapd.changes snapd-2.48.3+20.04/packaging/opensuse-15.0/snapd.changes --- snapd-2.48+20.04/packaging/opensuse-15.0/snapd.changes 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse-15.0/snapd.changes 2021-02-02 08:21:12.000000000 +0000 @@ -1,4 +1,19 @@ ------------------------------------------------------------------- +Tue, 02 Feb 2021 09:24:52 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.3 + +------------------------------------------------------------------- +Tue, 15 Dec 2020 20:38:36 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.2 + +------------------------------------------------------------------- +Thu, 03 Dec 2020 17:46:49 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.1 + +------------------------------------------------------------------- Thu, 19 Nov 2020 17:55:24 +0100 - mvo@ubuntu.com - Update to upstream release 2.48 diff -Nru snapd-2.48+20.04/packaging/opensuse-15.0/snapd.spec snapd-2.48.3+20.04/packaging/opensuse-15.0/snapd.spec --- snapd-2.48+20.04/packaging/opensuse-15.0/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse-15.0/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -83,7 +83,7 @@ Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0 Summary: Tools enabling systems to work with .snap files License: GPL-3.0 diff -Nru snapd-2.48+20.04/packaging/opensuse-15.1/snapd.changes snapd-2.48.3+20.04/packaging/opensuse-15.1/snapd.changes --- snapd-2.48+20.04/packaging/opensuse-15.1/snapd.changes 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse-15.1/snapd.changes 2021-02-02 08:21:12.000000000 +0000 @@ -1,4 +1,19 @@ ------------------------------------------------------------------- +Tue, 02 Feb 2021 09:24:52 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.3 + +------------------------------------------------------------------- +Tue, 15 Dec 2020 20:38:36 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.2 + +------------------------------------------------------------------- +Thu, 03 Dec 2020 17:46:49 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.1 + +------------------------------------------------------------------- Thu, 19 Nov 2020 17:55:24 +0100 - mvo@ubuntu.com - Update to upstream release 2.48 diff -Nru snapd-2.48+20.04/packaging/opensuse-15.1/snapd.spec snapd-2.48.3+20.04/packaging/opensuse-15.1/snapd.spec --- snapd-2.48+20.04/packaging/opensuse-15.1/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse-15.1/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -83,7 +83,7 @@ Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0 Summary: Tools enabling systems to work with .snap files License: GPL-3.0 diff -Nru snapd-2.48+20.04/packaging/opensuse-15.2/snapd.changes snapd-2.48.3+20.04/packaging/opensuse-15.2/snapd.changes --- snapd-2.48+20.04/packaging/opensuse-15.2/snapd.changes 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse-15.2/snapd.changes 2021-02-02 08:21:12.000000000 +0000 @@ -1,4 +1,19 @@ ------------------------------------------------------------------- +Tue, 02 Feb 2021 09:24:52 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.3 + +------------------------------------------------------------------- +Tue, 15 Dec 2020 20:38:36 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.2 + +------------------------------------------------------------------- +Thu, 03 Dec 2020 17:46:49 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.1 + +------------------------------------------------------------------- Thu, 19 Nov 2020 17:55:24 +0100 - mvo@ubuntu.com - Update to upstream release 2.48 diff -Nru snapd-2.48+20.04/packaging/opensuse-15.2/snapd.spec snapd-2.48.3+20.04/packaging/opensuse-15.2/snapd.spec --- snapd-2.48+20.04/packaging/opensuse-15.2/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse-15.2/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -83,7 +83,7 @@ Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0 Summary: Tools enabling systems to work with .snap files License: GPL-3.0 diff -Nru snapd-2.48+20.04/packaging/opensuse-tumbleweed/snapd.changes snapd-2.48.3+20.04/packaging/opensuse-tumbleweed/snapd.changes --- snapd-2.48+20.04/packaging/opensuse-tumbleweed/snapd.changes 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse-tumbleweed/snapd.changes 2021-02-02 08:21:12.000000000 +0000 @@ -1,4 +1,19 @@ ------------------------------------------------------------------- +Tue, 02 Feb 2021 09:24:52 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.3 + +------------------------------------------------------------------- +Tue, 15 Dec 2020 20:38:36 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.2 + +------------------------------------------------------------------- +Thu, 03 Dec 2020 17:46:49 +0100 - mvo@ubuntu.com + +- Update to upstream release 2.48.1 + +------------------------------------------------------------------- Thu, 19 Nov 2020 17:55:24 +0100 - mvo@ubuntu.com - Update to upstream release 2.48 diff -Nru snapd-2.48+20.04/packaging/opensuse-tumbleweed/snapd.spec snapd-2.48.3+20.04/packaging/opensuse-tumbleweed/snapd.spec --- snapd-2.48+20.04/packaging/opensuse-tumbleweed/snapd.spec 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/opensuse-tumbleweed/snapd.spec 2021-02-02 08:21:12.000000000 +0000 @@ -83,7 +83,7 @@ Name: snapd -Version: 2.48 +Version: 2.48.3 Release: 0 Summary: Tools enabling systems to work with .snap files License: GPL-3.0 diff -Nru snapd-2.48+20.04/packaging/ubuntu-14.04/changelog snapd-2.48.3+20.04/packaging/ubuntu-14.04/changelog --- snapd-2.48+20.04/packaging/ubuntu-14.04/changelog 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/ubuntu-14.04/changelog 2021-02-02 08:21:12.000000000 +0000 @@ -1,3 +1,68 @@ +snapd (2.48.3~14.04) trusty; urgency=medium + + * SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + * interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + + -- Michael Vogt Tue, 02 Feb 2021 09:21:12 +0100 + +snapd (2.48.2~14.04) trusty; urgency=medium + + * New upstream release, LP: #1906690 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + + -- Michael Vogt Tue, 15 Dec 2020 20:21:44 +0100 + +snapd (2.48.1~14.04) trusty; urgency=medium + + * New upstream release, LP: #1906690 + - gadget: disable ubuntu-boot role validation check + + -- Michael Vogt Thu, 03 Dec 2020 17:43:30 +0100 + snapd (2.48~14.04) trusty; urgency=medium * New upstream release, LP: #1904098 diff -Nru snapd-2.48+20.04/packaging/ubuntu-16.04/changelog snapd-2.48.3+20.04/packaging/ubuntu-16.04/changelog --- snapd-2.48+20.04/packaging/ubuntu-16.04/changelog 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/packaging/ubuntu-16.04/changelog 2021-02-02 08:21:12.000000000 +0000 @@ -1,4 +1,69 @@ -snapd (2.48+20.04) focal; urgency=medium +snapd (2.48.3+20.04) focal-security; urgency=medium + + * SECURITY UPDATE: sandbox escape vulnerability for containers + (LP: #1910456) + - many: add Delegate=true to generated systemd units for special + interfaces + - interfaces/greengrass-support: back-port interface changes to + 2.48 + - CVE-2020-27352 + * interfaces/builtin/docker-support: allow /run/containerd/s/... + - This is a new path that docker 19.03.14 (with a new version of + containerd) uses to avoid containerd CVE issues around the unix + socket. See also CVE-2020-15257. + + -- Michael Vogt Tue, 02 Feb 2021 09:21:12 +0100 + +snapd (2.48.2) xenial; urgency=medium + + * New upstream release, LP: #1906690 + - tests: sign new nested-18|20* models to allow for generic serials + - secboot: add extra paranoia when waiting for that fde-reveal-key + - tests: backport netplan workarounds from #9785 + - secboot: add workaround for snapcore/core-initrd issue #13 + - devicestate: log checkEncryption errors via logger.Noticef + - tests: add nested spread end-to-end test for fde-hooks + - devicestate: implement checkFDEFeatures() + - boot: tweak resealing with fde-setup hooks + - sysconfig/cloudinit.go: add "manual_cache_clean: true" to cloud- + init restrict file + - secboot: add new LockSealedKeys() that uses either TPM or + fde-reveal-key + - gadget: use "sealed-keys" to determine what method to use for + reseal + - boot: add sealKeyToModeenvUsingFdeSetupHook() + - secboot: use `fde-reveal-key` if available to unseal key + - cmd/snap-update-ns: fix sorting of overname mount entries wrt + other entries + - o/devicestate: save model with serial in the device save db + - devicestate: add runFDESetupHook() helper + - secboot,devicestate: add scaffoling for "fde-reveal-key" support + - hookstate: add new HookManager.EphemeralRunHook() + - update-pot: fix typo in plural keyword spec + - store,cmd/snap-repair: increase initial expontential time + intervals + - o/devicestate,daemon: fix reboot system action to not require a + system label + - github: run nested suite when commit is pushed to release branch + - tests: reset fakestore unit status + - tests: fix uc20-create-parition-* tests for updated gadget + - hookstate: implement snapctl fde-setup-{request,result} + - devicestate: make checkEncryption fde-setup hook aware + - client,snapctl: add naive support for "stdin" + - devicestate: support "storage-safety" defaults during install + - snap: use the boot-base for kernel hooks + - vendor: update secboot repo to avoid including secboot.test binary + + -- Michael Vogt Tue, 15 Dec 2020 20:21:44 +0100 + +snapd (2.48.1) xenial; urgency=medium + + * New upstream release, LP: #1906690 + - gadget: disable ubuntu-boot role validation check + + -- Michael Vogt Thu, 03 Dec 2020 17:43:30 +0100 + +snapd (2.48) xenial; urgency=medium * New upstream release, LP: #1904098 - osutil: add KernelCommandLineKeyValue diff -Nru snapd-2.48+20.04/run-checks snapd-2.48.3+20.04/run-checks --- snapd-2.48+20.04/run-checks 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/run-checks 2021-02-02 08:21:12.000000000 +0000 @@ -241,12 +241,14 @@ git ls-files -z -- . ':!:./po' ':!:./vendor' | xargs -0 misspell -error -i "$MISSPELL_IGNORE" - echo "Checking for ineffective assignments" - if ! command -v ineffassign >/dev/null; then - go get -u github.com/gordonklaus/ineffassign + if dpkg --compare-versions "$(go version | awk '$3 ~ /^go[0-9]/ {print substr($3, 3)}')" ge 1.12; then + echo "Checking for ineffective assignments" + if ! command -v ineffassign >/dev/null; then + go get -u github.com/gordonklaus/ineffassign + fi + # ineffassign knows about ignoring vendor/ \o/ + ineffassign ./... fi - # ineffassign knows about ignoring vendor/ \o/ - ineffassign . echo "Checking for naked returns" if ! command -v nakedret >/dev/null; then diff -Nru snapd-2.48+20.04/secboot/export_test.go snapd-2.48.3+20.04/secboot/export_test.go --- snapd-2.48+20.04/secboot/export_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/secboot/export_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -22,12 +22,14 @@ import ( "io" + "time" sb "github.com/snapcore/secboot" ) var ( EFIImageFromBootFile = efiImageFromBootFile + LockTPMSealedKeys = lockTPMSealedKeys ) func MockSbConnectToDefaultTPM(f func() (*sb.TPMConnection, error)) (restore func()) { @@ -177,3 +179,35 @@ isTPMEnabled = old } } + +func MockFDEHasRevealKey(f func() bool) (restore func()) { + old := FDEHasRevealKey + FDEHasRevealKey = f + return func() { + FDEHasRevealKey = old + } +} + +func MockFdeRevealKeyCommandExtra(args []string) (restore func()) { + oldFdeRevealKeyCommandExtra := fdeRevealKeyCommandExtra + fdeRevealKeyCommandExtra = args + return func() { + fdeRevealKeyCommandExtra = oldFdeRevealKeyCommandExtra + } +} + +func MockFdeRevealKeyRuntimeMax(d time.Duration) (restore func()) { + oldFdeRevealKeyRuntimeMax := fdeRevealKeyRuntimeMax + fdeRevealKeyRuntimeMax = d + return func() { + fdeRevealKeyRuntimeMax = oldFdeRevealKeyRuntimeMax + } +} + +func MockFdeRevealKeyPollWaitParanoiaFactor(n int) (restore func()) { + oldFdeRevealKeyPollWaitParanoiaFactor := fdeRevealKeyPollWaitParanoiaFactor + fdeRevealKeyPollWaitParanoiaFactor = n + return func() { + fdeRevealKeyPollWaitParanoiaFactor = oldFdeRevealKeyPollWaitParanoiaFactor + } +} diff -Nru snapd-2.48+20.04/secboot/secboot.go snapd-2.48.3+20.04/secboot/secboot.go --- snapd-2.48+20.04/secboot/secboot.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/secboot/secboot.go 2021-02-02 08:21:12.000000000 +0000 @@ -143,3 +143,7 @@ // - UnlockedWithKey UnlockMethod UnlockMethod } + +// FDEHasReveal is setup by devicestate/fde to support device-specific +// full disk encryption implementations. +var FDEHasRevealKey = func() bool { return false } diff -Nru snapd-2.48+20.04/secboot/secboot_tpm.go snapd-2.48.3+20.04/secboot/secboot_tpm.go --- snapd-2.48+20.04/secboot/secboot_tpm.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/secboot/secboot_tpm.go 2021-02-02 08:21:12.000000000 +0000 @@ -21,12 +21,16 @@ package secboot import ( + "bytes" "crypto/rand" + "encoding/json" "errors" "fmt" "io/ioutil" "os" + "os/exec" "path/filepath" + "time" "github.com/canonical/go-tpm2" sb "github.com/snapcore/secboot" @@ -35,6 +39,7 @@ "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/bootloader" "github.com/snapcore/snapd/bootloader/efi" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/osutil/disks" @@ -185,11 +190,32 @@ return nil } -// LockTPMSealedKeys manually locks access to the sealed keys. Meant to be +// LockSealedKeys manually locks access to the sealed keys. Meant to be // called in place of passing lockKeysOnFinish as true to // UnlockVolumeUsingSealedKeyIfEncrypted for cases where we don't know if a // given call is the last one to unlock a volume like in degraded recover mode. -func LockTPMSealedKeys() error { +func LockSealedKeys() error { + if FDEHasRevealKey() { + return lockFDERevealSealedKeys() + } + return lockTPMSealedKeys() +} + +func lockFDERevealSealedKeys() error { + buf, err := json.Marshal(FDERevealKeyRequest{ + Op: "lock", + }) + if err != nil { + return fmt.Errorf(`cannot build request for fde-reveal-key "lock": %v`, err) + } + if output, err := runFDERevealKeyCommand(buf); err != nil { + return fmt.Errorf(`cannot run fde-reveal-key "lock": %v`, osutil.OutputErr(output, err)) + } + + return nil +} + +func lockTPMSealedKeys() error { tpm, tpmErr := sbConnectToDefaultTPM() if tpmErr != nil { if xerrors.Is(tpmErr, sb.ErrNoTPM2Device) { @@ -224,20 +250,8 @@ // whether there is an encrypted device or not, IsEncrypted on the return // value will be true, even if error is non-nil. This is so that callers can be // robust and try unlocking using another method for example. -func UnlockVolumeUsingSealedKeyIfEncrypted( - disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *UnlockVolumeUsingSealedKeyOptions, -) (UnlockResult, error) { - res := UnlockResult{ - UnlockMethod: NotUnlocked, - } - - if opts == nil { - opts = &UnlockVolumeUsingSealedKeyOptions{} - } - // TODO:UC20: use sb.SecureConnectToDefaultTPM() if we decide there's benefit in doing that or - // we have a hard requirement for a valid EK cert chain for every boot (ie, panic - // if there isn't one). But we can't do that as long as we need to download - // intermediate certs from the manufacturer. +func UnlockVolumeUsingSealedKeyIfEncrypted(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *UnlockVolumeUsingSealedKeyOptions) (UnlockResult, error) { + res := UnlockResult{} // find the encrypted device using the disk we were provided - note that // we do not specify IsDecryptedDevice in opts because here we are @@ -262,17 +276,204 @@ } } - res.PartDevice = filepath.Join("/dev/disk/by-partuuid", partUUID) + partDevice := filepath.Join("/dev/disk/by-partuuid", partUUID) if !res.IsEncrypted { // if we didn't find an encrypted device just return, don't try to // unlock it // the filesystem device for the unencrypted case is the same as the // partition device + res.PartDevice = partDevice res.FsDevice = res.PartDevice return res, nil } + mapperName := name + "-" + randutilRandomKernelUUID() + sourceDevice := partDevice + targetDevice := filepath.Join("/dev/mapper", mapperName) + + if FDEHasRevealKey() { + return unlockVolumeUsingSealedKeyFDERevealKey(name, sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts) + } else { + return unlockVolumeUsingSealedKeySecboot(name, sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts) + } +} + +// FDERevealKeyRequest carries the operation and parameters for the +// fde-reveal-key binary to support unsealing keys that were sealed +// with the "fde-setup" hook. +type FDERevealKeyRequest struct { + Op string `json:"op"` + + SealedKey []byte `json:"sealed-key,omitempty"` + KeyName string `json:"key-name,omitempty"` + + // TODO: add VolumeName,SourceDevicePath later +} + +// fdeRevealKeyRuntimeMax is the maximum runtime a fde-reveal-key can execute +// XXX: what is a reasonable default here? +var fdeRevealKeyRuntimeMax = 2 * time.Minute + +// 50 ms means we check at a frequency 20 Hz, fast enough to not hold +// up boot, but not too fast that we are hogging the CPU from the +// thing we are waiting to finish running +var fdeRevealKeyPollWait = 50 * time.Millisecond + +// fdeRevealKeyPollWaitParanoiaFactor controls much longer we wait +// then fdeRevealKeyRuntimeMax before stopping to poll for results +var fdeRevealKeyPollWaitParanoiaFactor = 2 + +// overridden in tests +var fdeRevealKeyCommandExtra []string + +// runFDERevealKeyCommand returns the output of fde-reveal-key run +// with systemd. +// +// Note that systemd-run in the initrd can only talk to the private +// systemd bus so this cannot use "--pipe" or "--wait", see +// https://github.com/snapcore/core-initrd/issues/13 +func runFDERevealKeyCommand(stdin []byte) (output []byte, err error) { + runDir := filepath.Join(dirs.GlobalRootDir, "/run/fde-reveal-key") + if err := os.MkdirAll(runDir, 0700); err != nil { + return nil, fmt.Errorf("cannot create tmp dir for fde-reveal-key: %v", err) + } + + // delete and re-create the std{in,out,err} stream files that we use for the + // hook to be robust against bugs where the files are created with too + // permissive permissions or not properly deleted afterwards since the hook + // will be invoked multiple times during the initrd and we want to be really + // careful since the stdout file will contain the unsealed encryption key + for _, stream := range []string{"stdin", "stdout", "stderr"} { + streamFile := filepath.Join(runDir, "fde-reveal-key."+stream) + // we want to make sure that the file permissions for stdout are always + // 0600, so to ensure this is the case and be robust against bugs, we + // always delete the file and re-create it with 0600 + + // note that if the file already exists, WriteFile will not change the + // permissions, so deleting first is the right thing to do + os.Remove(streamFile) + if stream == "stdin" { + err = ioutil.WriteFile(streamFile, stdin, 0600) + } else { + err = ioutil.WriteFile(streamFile, nil, 0600) + } + if err != nil { + return nil, fmt.Errorf("cannot create %s for fde-reveal-key: %v", stream, err) + } + } + + // TODO: put this into a new "systemd/run" package + cmd := exec.Command( + "systemd-run", + "--collect", + "--service-type=exec", + "--quiet", + // ensure we get some result from the hook within a + // reasonable timeout and output from systemd if + // things go wrong + fmt.Sprintf("--property=RuntimeMaxSec=%s", fdeRevealKeyRuntimeMax), + // Do not allow mounting, this ensures hooks in initrd + // can not mess around with ubuntu-data. + // + // Note that this is not about perfect confinement, more about + // making sure that people using the hook know that we do not + // want them to mess around outside of just providing unseal. + "--property=SystemCallFilter=~@mount", + // WORKAROUNDS + // workaround the lack of "--pipe" + fmt.Sprintf("--property=StandardInput=file:%s/fde-reveal-key.stdin", runDir), + // NOTE: these files are manually created above with 0600 because by + // default systemd will create them 0644 and we want to be paranoid here + fmt.Sprintf("--property=StandardOutput=file:%s/fde-reveal-key.stdout", runDir), + fmt.Sprintf("--property=StandardError=file:%s/fde-reveal-key.stderr", runDir), + // this ensures we get useful output for e.g. segfaults + fmt.Sprintf(`--property=ExecStopPost=/bin/sh -c 'if [ "$EXIT_STATUS" = 0 ]; then touch %[1]s/fde-reveal-key.success; else echo "service result: $SERVICE_RESULT" >%[1]s/fde-reveal-key.failed; fi'`, runDir), + ) + if fdeRevealKeyCommandExtra != nil { + cmd.Args = append(cmd.Args, fdeRevealKeyCommandExtra...) + } + // fde-reveal-key is what we actually need to run + cmd.Args = append(cmd.Args, "fde-reveal-key") + + // ensure we cleanup our tmp files + defer func() { + if err := os.RemoveAll(runDir); err != nil { + logger.Noticef("cannot remove tmp dir: %v", err) + } + }() + + // run the command + output, err = cmd.CombinedOutput() + if err != nil { + return output, err + } + + // This loop will be terminate by systemd-run, either because + // fde-reveal-key exists or it gets killed when it reaches the + // fdeRevealKeyRuntimeMax defined above. + // + // However we are paranoid and exit this loop if systemd + // did not terminate the process after twice the allocated + // runtime + maxLoops := int(fdeRevealKeyRuntimeMax/fdeRevealKeyPollWait) * fdeRevealKeyPollWaitParanoiaFactor + for i := 0; i < maxLoops; i++ { + switch { + case osutil.FileExists(filepath.Join(runDir, "fde-reveal-key.failed")): + stderr, _ := ioutil.ReadFile(filepath.Join(runDir, "fde-reveal-key.stderr")) + systemdErr, _ := ioutil.ReadFile(filepath.Join(runDir, "fde-reveal-key.failed")) + buf := bytes.NewBuffer(stderr) + buf.Write(systemdErr) + return buf.Bytes(), fmt.Errorf("fde-reveal-key failed") + case osutil.FileExists(filepath.Join(runDir, "fde-reveal-key.success")): + return ioutil.ReadFile(filepath.Join(runDir, "fde-reveal-key.stdout")) + default: + time.Sleep(fdeRevealKeyPollWait) + } + } + + // this should never happen, the loop above should be terminated + // via systemd + return nil, fmt.Errorf("internal error: systemd-run did not honor RuntimeMax=%s setting", fdeRevealKeyRuntimeMax) +} + +func unlockVolumeUsingSealedKeyFDERevealKey(name, sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName string, opts *UnlockVolumeUsingSealedKeyOptions) (UnlockResult, error) { + res := UnlockResult{IsEncrypted: true, PartDevice: sourceDevice} + + sealedKey, err := ioutil.ReadFile(sealedEncryptionKeyFile) + if err != nil { + return res, fmt.Errorf("cannot read sealed key file: %v", err) + } + buf, err := json.Marshal(FDERevealKeyRequest{ + Op: "reveal", + SealedKey: sealedKey, + KeyName: name, + }) + if err != nil { + return res, fmt.Errorf("cannot build request for fde-reveal-key: %v", err) + } + output, err := runFDERevealKeyCommand(buf) + if err != nil { + return res, fmt.Errorf("cannot run fde-reveal-key: %v", osutil.OutputErr(output, err)) + } + + // the output of fde-reveal-key is the unsealed key + unsealedKey := output + if err := unlockEncryptedPartitionWithKey(mapperName, sourceDevice, unsealedKey); err != nil { + return res, fmt.Errorf("cannot unlock encrypted partition: %v", err) + } + res.FsDevice = targetDevice + res.UnlockMethod = UnlockedWithSealedKey + return res, nil +} + +func unlockVolumeUsingSealedKeySecboot(name, sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName string, opts *UnlockVolumeUsingSealedKeyOptions) (UnlockResult, error) { + // TODO:UC20: use sb.SecureConnectToDefaultTPM() if we decide there's benefit in doing that or + // we have a hard requirement for a valid EK cert chain for every boot (ie, panic + // if there isn't one). But we can't do that as long as we need to download + // intermediate certs from the manufacturer. + + res := UnlockResult{IsEncrypted: true, PartDevice: sourceDevice} // Obtain a TPM connection. tpm, tpmErr := sbConnectToDefaultTPM() if tpmErr != nil { @@ -288,10 +489,6 @@ // and endorsement hierarchies, but the device will remain visible to the operating system. tpmDeviceAvailable := tpmErr == nil && isTPMEnabled(tpm) - mapperName := name + "-" + randutilRandomKernelUUID() - sourceDevice := res.PartDevice - targetDevice := filepath.Join("/dev/mapper", mapperName) - // if we don't have a tpm, and we allow using a recovery key, do that // directly if !tpmDeviceAvailable && opts.AllowRecoveryKey { diff -Nru snapd-2.48+20.04/secboot/secboot_tpm_test.go snapd-2.48.3+20.04/secboot/secboot_tpm_test.go --- snapd-2.48+20.04/secboot/secboot_tpm_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/secboot/secboot_tpm_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -22,13 +22,16 @@ import ( "crypto/ecdsa" + "encoding/base64" "errors" "fmt" "io" "io/ioutil" "os" + "os/exec" "path/filepath" "testing" + "time" "github.com/canonical/go-tpm2" sb "github.com/snapcore/secboot" @@ -56,7 +59,10 @@ var _ = Suite(&secbootSuite{}) func (s *secbootSuite) SetUpTest(c *C) { - dirs.SetRootDir(c.MkDir()) + rootDir := c.MkDir() + err := os.MkdirAll(filepath.Join(rootDir, "/run"), 0755) + c.Assert(err, IsNil) + dirs.SetRootDir(rootDir) s.AddCleanup(func() { dirs.SetRootDir("/") }) } @@ -1152,3 +1158,290 @@ FsDevice: "", }) } + +func (s *secbootSuite) TestUnlockVolumeUsingSealedKeyIfEncryptedFdeRevealKeyTruncatesStreamFiles(c *C) { + // this test uses a real systemd-run --user so check here if that + // actually works + if output, err := exec.Command("systemd-run", "--user", "--wait", "--collect", "true").CombinedOutput(); err != nil { + c.Skip(fmt.Sprintf("systemd-run not working: %v", osutil.OutputErr(output, err))) + } + + // create the temporary output file streams with garbage data to ensure that + // by the time the hook runs the files are emptied and recreated with the + // right permissions + streamFiles := []string{} + for _, stream := range []string{"stdin", "stdout", "stderr"} { + streamFile := filepath.Join(dirs.GlobalRootDir, "/run/fde-reveal-key/fde-reveal-key."+stream) + streamFiles = append(streamFiles, streamFile) + // make the dir 0700 + err := os.MkdirAll(filepath.Dir(streamFile), 0700) + c.Assert(err, IsNil) + // but make the file world-readable as it should be reset to 0600 before + // the hook is run + err = ioutil.WriteFile(streamFile, []byte("blah blah blah blah blah blah blah blah blah blah"), 0755) + c.Assert(err, IsNil) + } + + restore := secboot.MockFDEHasRevealKey(func() bool { + return true + }) + defer restore() + + // the hook script only verifies that the stdout file is empty since we + // need to write to the stderr file for performing the test, but we still + // check the stderr file for correct permissions + mockSystemdRun := testutil.MockCommand(c, "fde-reveal-key", fmt.Sprintf(` +# check that stdin has the right sealed key content +if [ "$(cat %[1]s)" != "{\"op\":\"reveal\",\"sealed-key\":\"AQIDBA==\",\"key-name\":\"name\"}" ]; then + echo "test failed: stdin file has wrong content: $(cat %[1]s)" 1>&2 +else + echo "stdin file has correct content" 1>&2 +fi + +# check that stdout is empty +if [ -n "$(cat %[2]s)" ]; then + echo "test failed: stdout file is not empty: $(cat %[2]s)" 1>&2 +else + echo "stdout file is correctly empty" 1>&2 +fi + +# check that stdin has the right 600 perms +if [ "$(stat --format=%%a %[1]s)" != "600" ]; then + echo "test failed: stdin file has wrong permissions: $(stat --format=%%a %[1]s)" 1>&2 +else + echo "stdin file has correct 600 permissions" 1>&2 +fi + +# check that stdout has the right 600 perms +if [ "$(stat --format=%%a %[2]s)" != "600" ]; then + echo "test failed: stdout file has wrong permissions: $(stat --format=%%a %[2]s)" 1>&2 +else + echo "stdout file has correct 600 permissions" 1>&2 +fi + +# check that stderr has the right 600 perms +if [ "$(stat --format=%%a %[3]s)" != "600" ]; then + echo "test failed: stderr file has wrong permissions: $(stat --format=%%a %[3]s)" 1>&2 +else + echo "stderr file has correct 600 permissions" 1>&2 +fi + +echo "making the hook always fail for simpler test code" 1>&2 + +# always make the hook exit 1 for simpler test code +exit 1 +`, streamFiles[0], streamFiles[1], streamFiles[2])) + defer mockSystemdRun.Restore() + restore = secboot.MockFdeRevealKeyCommandExtra([]string{"--user"}) + defer restore() + + mockDiskWithEncDev := &disks.MockDiskMapping{ + FilesystemLabelToPartUUID: map[string]string{ + "name-enc": "enc-dev-partuuid", + }, + } + defaultDevice := "name" + mockSealedKeyFile := filepath.Join(c.MkDir(), "vanilla-keyfile") + err := ioutil.WriteFile(mockSealedKeyFile, []byte{1, 2, 3, 4}, 0600) + c.Assert(err, IsNil) + + opts := &secboot.UnlockVolumeUsingSealedKeyOptions{} + _, err = secboot.UnlockVolumeUsingSealedKeyIfEncrypted(mockDiskWithEncDev, defaultDevice, mockSealedKeyFile, opts) + c.Assert(err, ErrorMatches, `(?s)cannot run fde-reveal-key: +----- +stdin file has correct content +stdout file is correctly empty +stdin file has correct 600 permissions +stdout file has correct 600 permissions +stderr file has correct 600 permissions +making the hook always fail for simpler test code +service result: exit-code +-----`) + // ensure no tmp files are left behind + c.Check(osutil.FileExists(filepath.Join(dirs.GlobalRootDir, "/run/fde-reveal-key")), Equals, false) +} + +func (s *secbootSuite) TestUnlockVolumeUsingSealedKeyIfEncryptedFdeRevealKeyErr(c *C) { + // this test uses a real systemd-run --user so check here if that + // actually works + if output, err := exec.Command("systemd-run", "--user", "--wait", "--collect", "true").CombinedOutput(); err != nil { + c.Skip(fmt.Sprintf("systemd-run not working: %v", osutil.OutputErr(output, err))) + } + + restore := secboot.MockFDEHasRevealKey(func() bool { + return true + }) + defer restore() + + mockSystemdRun := testutil.MockCommand(c, "fde-reveal-key", `echo failed 1>&2; false`) + defer mockSystemdRun.Restore() + restore = secboot.MockFdeRevealKeyCommandExtra([]string{"--user"}) + defer restore() + + mockDiskWithEncDev := &disks.MockDiskMapping{ + FilesystemLabelToPartUUID: map[string]string{ + "name-enc": "enc-dev-partuuid", + }, + } + defaultDevice := "name" + mockSealedKeyFile := filepath.Join(c.MkDir(), "vanilla-keyfile") + err := ioutil.WriteFile(mockSealedKeyFile, []byte{1, 2, 3, 4}, 0600) + c.Assert(err, IsNil) + + opts := &secboot.UnlockVolumeUsingSealedKeyOptions{} + _, err = secboot.UnlockVolumeUsingSealedKeyIfEncrypted(mockDiskWithEncDev, defaultDevice, mockSealedKeyFile, opts) + c.Assert(err, ErrorMatches, `(?s)cannot run fde-reveal-key: +----- +failed +service result: exit-code +-----`) + // ensure no tmp files are left behind + c.Check(osutil.FileExists(filepath.Join(dirs.GlobalRootDir, "/run/fde-reveal-key")), Equals, false) +} + +func (s *secbootSuite) TestUnlockVolumeUsingSealedKeyIfEncryptedFdeRevealKey(c *C) { + // this test uses a real systemd-run --user so check here if that + // actually works + if output, err := exec.Command("systemd-run", "--user", "--wait", "--collect", "true").CombinedOutput(); err != nil { + c.Skip(fmt.Sprintf("systemd-run not working: %v", osutil.OutputErr(output, err))) + } + + restore := secboot.MockFDEHasRevealKey(func() bool { + return true + }) + defer restore() + + restore = secboot.MockRandomKernelUUID(func() string { + return "random-uuid-for-test" + }) + defer restore() + + mockDiskWithEncDev := &disks.MockDiskMapping{ + FilesystemLabelToPartUUID: map[string]string{ + "name-enc": "enc-dev-partuuid", + }, + } + + restore = secboot.MockFdeRevealKeyCommandExtra([]string{"--user"}) + defer restore() + fdeRevealKeyStdin := filepath.Join(c.MkDir(), "stdin") + mockSystemdRun := testutil.MockCommand(c, "fde-reveal-key", fmt.Sprintf(` +cat - > %s +printf "unsealed-key-from-hook" +`, fdeRevealKeyStdin)) + defer mockSystemdRun.Restore() + + restore = secboot.MockSbActivateVolumeWithKey(func(volumeName, sourceDevicePath string, key []byte, options *sb.ActivateVolumeOptions) error { + c.Check(string(key), Equals, "unsealed-key-from-hook") + return nil + }) + defer restore() + + defaultDevice := "name" + mockSealedKeyFile := filepath.Join(c.MkDir(), "vanilla-keyfile") + err := ioutil.WriteFile(mockSealedKeyFile, []byte("sealed-key"), 0600) + c.Assert(err, IsNil) + + opts := &secboot.UnlockVolumeUsingSealedKeyOptions{} + res, err := secboot.UnlockVolumeUsingSealedKeyIfEncrypted(mockDiskWithEncDev, defaultDevice, mockSealedKeyFile, opts) + c.Assert(err, IsNil) + c.Check(res, DeepEquals, secboot.UnlockResult{ + UnlockMethod: secboot.UnlockedWithSealedKey, + IsEncrypted: true, + PartDevice: "/dev/disk/by-partuuid/enc-dev-partuuid", + FsDevice: "/dev/mapper/name-random-uuid-for-test", + }) + c.Check(mockSystemdRun.Calls(), DeepEquals, [][]string{ + {"fde-reveal-key"}, + }) + c.Check(fdeRevealKeyStdin, testutil.FileEquals, fmt.Sprintf(`{"op":"reveal","sealed-key":%q,"key-name":"name"}`, base64.StdEncoding.EncodeToString([]byte("sealed-key")))) + + // ensure no tmp files are left behind + c.Check(osutil.FileExists(filepath.Join(dirs.GlobalRootDir, "/run/fde-reveal-key")), Equals, false) +} + +func (s *secbootSuite) TestLockSealedKeysCallsFdeReveal(c *C) { + // this test uses a real systemd-run --user so check here if that + // actually works + if output, err := exec.Command("systemd-run", "--user", "--wait", "--collect", "true").CombinedOutput(); err != nil { + c.Skip(fmt.Sprintf("systemd-run not working: %v", osutil.OutputErr(output, err))) + } + + restore := secboot.MockFDEHasRevealKey(func() bool { + return true + }) + defer restore() + + restore = secboot.MockFdeRevealKeyCommandExtra([]string{"--user"}) + defer restore() + fdeRevealKeyStdin := filepath.Join(c.MkDir(), "stdin") + mockSystemdRun := testutil.MockCommand(c, "fde-reveal-key", fmt.Sprintf(` +cat - > %s +`, fdeRevealKeyStdin)) + defer mockSystemdRun.Restore() + + err := secboot.LockSealedKeys() + c.Assert(err, IsNil) + c.Check(mockSystemdRun.Calls(), DeepEquals, [][]string{ + {"fde-reveal-key"}, + }) + c.Check(fdeRevealKeyStdin, testutil.FileEquals, `{"op":"lock"}`) + + // ensure no tmp files are left behind + c.Check(osutil.FileExists(filepath.Join(dirs.GlobalRootDir, "/run/fde-reveal-key")), Equals, false) +} + +func (s *secbootSuite) TestLockSealedKeysHonorsRuntimeMax(c *C) { + // this test uses a real systemd-run --user so check here if that + // actually works + if output, err := exec.Command("systemd-run", "--user", "--wait", "--collect", "true").CombinedOutput(); err != nil { + c.Skip(fmt.Sprintf("systemd-run not working: %v", osutil.OutputErr(output, err))) + } + + restore := secboot.MockFDEHasRevealKey(func() bool { + return true + }) + defer restore() + + restore = secboot.MockFdeRevealKeyCommandExtra([]string{"--user"}) + defer restore() + mockSystemdRun := testutil.MockCommand(c, "fde-reveal-key", "sleep 60") + defer mockSystemdRun.Restore() + + restore = secboot.MockFdeRevealKeyPollWaitParanoiaFactor(100) + defer restore() + + restore = secboot.MockFdeRevealKeyRuntimeMax(100 * time.Millisecond) + defer restore() + + err := secboot.LockSealedKeys() + c.Assert(err, ErrorMatches, `cannot run fde-reveal-key "lock": service result: timeout`) +} + +func (s *secbootSuite) TestLockSealedKeysHonorsParanoia(c *C) { + // this test uses a real systemd-run --user so check here if that + // actually works + if output, err := exec.Command("systemd-run", "--user", "--wait", "--collect", "true").CombinedOutput(); err != nil { + c.Skip(fmt.Sprintf("systemd-run not working: %v", osutil.OutputErr(output, err))) + } + + restore := secboot.MockFDEHasRevealKey(func() bool { + return true + }) + defer restore() + + restore = secboot.MockFdeRevealKeyCommandExtra([]string{"--user"}) + defer restore() + mockSystemdRun := testutil.MockCommand(c, "fde-reveal-key", "sleep 60") + defer mockSystemdRun.Restore() + + restore = secboot.MockFdeRevealKeyPollWaitParanoiaFactor(1) + defer restore() + + // shorter than the fdeRevealKeyPollWait time + restore = secboot.MockFdeRevealKeyRuntimeMax(1 * time.Millisecond) + defer restore() + + err := secboot.LockSealedKeys() + c.Assert(err, ErrorMatches, `cannot run fde-reveal-key "lock": internal error: systemd-run did not honor RuntimeMax=1ms setting`) +} diff -Nru snapd-2.48+20.04/store/store.go snapd-2.48.3+20.04/store/store.go --- snapd-2.48+20.04/store/store.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/store/store.go 2021-02-02 08:21:12.000000000 +0000 @@ -69,7 +69,7 @@ // Timeout value var defaultRetryStrategy = retry.LimitCount(6, retry.LimitTime(38*time.Second, retry.Exponential{ - Initial: 350 * time.Millisecond, + Initial: 500 * time.Millisecond, Factor: 2.5, }, )) diff -Nru snapd-2.48+20.04/sysconfig/cloudinit.go snapd-2.48.3+20.04/sysconfig/cloudinit.go --- snapd-2.48+20.04/sysconfig/cloudinit.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/sysconfig/cloudinit.go 2021-02-02 08:21:12.000000000 +0000 @@ -148,12 +148,31 @@ cloudInitSnapdRestrictFile = "/etc/cloud/cloud.cfg.d/zzzz_snapd.cfg" cloudInitDisabledFile = "/etc/cloud/cloud-init.disabled" + // for NoCloud datasource, we need to specify "manual_cache_clean: true" + // because the default is false, and this key being true essentially informs + // cloud-init that it should always trust the instance-id it has cached in + // the image, and shouldn't assume that there is a new one on every boot, as + // otherwise we have bugs like https://bugs.launchpad.net/snapd/+bug/1905983 + // where subsequent boots after cloud-init runs and gets restricted it will + // try to detect the instance_id by reading from the NoCloud datasource + // fs_label, but we set that to "null" so it fails to read anything and thus + // can't detect the effective instance_id and assumes it is different and + // applies default config which can overwrite valid config from the initial + // boot if that is not the default config + // see also https://cloudinit.readthedocs.io/en/latest/topics/boot.html?highlight=manual_cache_clean#first-boot-determination nocloudRestrictYaml = []byte(`datasource_list: [NoCloud] datasource: NoCloud: - fs_label: null`) + fs_label: null +manual_cache_clean: true +`) - genericCloudRestrictYamlPattern = `datasource_list: [%s]` + // don't use manual_cache_clean for real cloud datasources, the setting is + // used with ubuntu core only for sources where we can only get the + // instance_id through the fs_label for NoCloud and None (since we disable + // importing using the fs_label after the initial run). + genericCloudRestrictYamlPattern = `datasource_list: [%s] +` localDatasources = []string{"NoCloud", "None"} ) diff -Nru snapd-2.48+20.04/sysconfig/cloudinit_test.go snapd-2.48.3+20.04/sysconfig/cloudinit_test.go --- snapd-2.48+20.04/sysconfig/cloudinit_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/sysconfig/cloudinit_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -370,7 +370,9 @@ var restrictNoCloudYaml = `datasource_list: [NoCloud] datasource: NoCloud: - fs_label: null` + fs_label: null +manual_cache_clean: true +` func (s *sysconfigSuite) TestRestrictCloudInit(c *C) { tt := []struct { @@ -435,12 +437,13 @@ expDisableFile: true, }, { - comment: "gce done", - state: sysconfig.CloudInitDone, - cloudInitStatusJSON: gceCloudInitStatusJSON, - expDatasource: "GCE", - expAction: "restrict", - expRestrictYamlWritten: "datasource_list: [GCE]", + comment: "gce done", + state: sysconfig.CloudInitDone, + cloudInitStatusJSON: gceCloudInitStatusJSON, + expDatasource: "GCE", + expAction: "restrict", + expRestrictYamlWritten: `datasource_list: [GCE] +`, }, { comment: "nocloud done", diff -Nru snapd-2.48+20.04/tests/core/fsck-on-boot/task.yaml snapd-2.48.3+20.04/tests/core/fsck-on-boot/task.yaml --- snapd-2.48+20.04/tests/core/fsck-on-boot/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/core/fsck-on-boot/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -35,6 +35,7 @@ fi umount /var/lib/snapd/seed umount /run/mnt/ubuntu-seed + umount /boot/efi else echo "Please adjust the test to support this core system" false diff -Nru snapd-2.48+20.04/tests/core/netplan/task.yaml snapd-2.48.3+20.04/tests/core/netplan/task.yaml --- snapd-2.48+20.04/tests/core/netplan/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/core/netplan/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -6,10 +6,21 @@ environment: NETPLAN: io.netplan.Netplan -# the test is only meaningful on core devices # TODO:UC20: enable, fails right now with: "The permission of the -# setuid helper is not correct" -systems: [ubuntu-core-1*] +# setuid helper is not correct", the issue is with the permissions of +# /usr/lib/dbus-1.0/dbus-daemon-launch-helper - it shows up as not +# being in the right group because the /etc/group from the classic +# host that we use to prepare the core system has a different group +# defined for gid 103 (which on core is messagebus, but on classic is +# systemd-network), and thus the dbus daemon thinks it has the wrong +# permissions (well basically speaking it does) + +# TODO: re-enable this on ubuntu-core-16-* when network-setup-control is updated +# to allow running netplan directly from the base snap and we can update this +# test to just using a simple shell script snap instead of building netplan from +# source, as currently that is broken on xenial with test-snapd-netplan-apply +# for some reason, see https://github.com/anonymouse64/test-snapd-netplan-apply/issues/1 +systems: [ubuntu-core-18-*] prepare: | snap install test-snapd-netplan-apply --edge diff -Nru snapd-2.48+20.04/tests/lib/assertions/CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez.auto-import.assert snapd-2.48.3+20.04/tests/lib/assertions/CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez.auto-import.assert --- snapd-2.48+20.04/tests/lib/assertions/CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez.auto-import.assert 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/assertions/CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez.auto-import.assert 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,76 @@ +type: account +authority-id: canonical +account-id: CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez +display-name: Samuele Pedroni +timestamp: 2017-01-04T10:39:56.778769Z +username: pedronis +validation: unproven +sign-key-sha3-384: BWDEoaqyr25nF5SNCvEv2v7QnM9QsfCc0PBMYD_i2NGSQ32EF2d4D0hqUel3m8ul + +AcLBUgQAAQoABgUCWGzQ/AAAhcwQAMj39rHTIyoxKCwu/riw/2ynWlIAeunUXr7vocaJFr22i6RD +3FR2viAREN8ljgWklE9l+1ZCX7gtz65V0Zv2efK9Fo/49A49VDecdWVWvMgGySG45l7Sq0ssgtYk +Zs1tGu69eNOosp2ZjX3Yq3gPeEeuVwssAmmPXgb+JoevIqT4Rj9KMnn7vwsgzOm8nVJVSGq6sovf +4SUq1QdiYU+/FqRB9/10LnwL/4PU2THh1Omw2eCUTzM5fE0gAlBzbcg35ryW6mQTE1gif9/EN/XT +kmcmI1mrVoSXRAxq032BkNjthRX1wfEXYtvdl72pr84PhbboVB4MWjch6cRNn0VOuCOsP+Yjqf5o +Pwz/WWOFzAkyLptWIaAFziT6OqvKxO+EEDKvbg0DHbiUiodMv1TJvbcEam/V5EVNee8ik8BZpWzT +VFNMriHxwZaMCcKJIpxEImH6xLNWXi6zklZIut4+zB+s51Z6NGtVV2KQmesEd1r3NOCjHoUV5VcM +hv817R4JTAk9pPPjr9yPylnvm1PPTvnkJnWEurfXIKZ1nhUbtKhvLK83bvoLMeUuqPcyjSve9w7D +5Ouv2h/48yl9AVZNOwj1M80XKZGJ4UGV54UIYIiGH23ewao3LlrMSNhtA1MO4ksddUZLVLqr4JJQ +YX6TIPBI36CeCa64IMYe5x5t3ard + +type: account-key +authority-id: canonical +public-key-sha3-384: x1Tnl94nkgb-rMDs_l63gvgkLYGjXKfpkCCIQZwJ72-LR6X6OrSvNS9z2WS1lAGz +account-id: CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez +name: test-models +since: 2020-05-11T19:07:32Z +body-length: 717 +sign-key-sha3-384: BWDEoaqyr25nF5SNCvEv2v7QnM9QsfCc0PBMYD_i2NGSQ32EF2d4D0hqUel3m8ul + +AcbBTQRWhcGAARAA0erwxWsJGPcfwaba/pb9cfISsVVdkZTEOjf1yMMSkGExIQwADoIKh1H66tXp +6vKaH9Js4KAIdek5ApvEsp5dD8VULrIBXdYoRAdcP41+puDM4GXHh+ly5ZJx37rVrmUM4/1TNBgZ +pEtDkvUqoGUduEt6oi1d9lqO9nPnIg0Vb7x6KmdI4sFwFi/1E22kZ4JPyoZMJ8ohI2yIXuGARMDu +MuAuvAxhy001c4T5NDko4Og40GE6gJFYyjGevzDo7ZnmBM8/8fTxkFgmo5aNcmRj/xteOSdW56I9 +DYECluu3iRa9g5jcpkQn42UTVlIFpSz0pByOXSZhPi4vNfs2rLQAW83U03PZTPCu9vWddahXtJYb +TXOG0nIkhHI+RzGEChZtFBt+YKAsVsmN92ATFK81O2wiiznV1mLeWJLw89Nzu+ROE5TcEpcqYAsH +8pVmH6SH3zcInLZYZdDMIFMaUpRpzflie8Xqo4cdTlhlDqZKzJdvtO97HB6YVGEVNvzRVMfHC+wU +Y/twfRDDTKSM7qtaoe28053tEl53V9bWHcMTKbqfTfXteqyG/CMNaT1voHxa+xHdFMSGr2mCpmre +mEIKTPk73WE1YTciimVSCxZzbbRU3PNG3JbCTHaZE5dWFohfep55Aq58Wb0RZKWXUNDuu2dWKEZA +pr57T/9ZbwwaF1kAEQEAAQ== + +AcLBUgQAAQoABgUCXrmiewAAecUQAAhrXWrbQD18k58cbBjUMnnwosFO3NwBJkKF0jreLODuiGCN +2xvdHfO9Li08uRmF8Plt39OH1w3d282W5dGHL+AAMFNpaJhzaXBfQnTTDfi6ow13sQieo7sLcxtt +T8ZiU8LKAtj+GgRLVEelkvVdIh2fBzVgZQlMMbre4319j6eQWkIS7GxO1fsTUZliLW9fP3mS9G3z +lUGsazVSUxAwnOSLm1mO9+uk1navsbWPufVr1onAHQ7Kqqr+ovB4N5GhcqpitGGHfijnH+7sMMR2 +XLjFQP5JkoqISgHgRcS3PDYyqigV44OBYiJOl+MwVQQ+uGtW/s9QJSTZlCEdIwVmFvtACH8Y3qPs +ttLVhklWqDah47BBPTQAUIa9I298bEpwCB3Yurnke1/iLv0kdv0CVNXnurOFja5gN3QEh47kp+aX +UQ2YCs8gyFtWvlxuMtcE6cUntPEDEtWpj0j8fjpnRLAcr42rG6YzGfS/vTW56j1L/lw9NrIDjTYL +GpSwEMPo6fNZi7i5WWVn+Rno54Rrrtxf/K0ZTaRAZGyVa43u+4vvRDXq0fmIZ+jaULZ43qdwOaS9 +gnriPJm1OLx/eyekYFJANDTQtto4afQbgXKBriCXALzrw8jwznZQuaFqVBwfFbNvKLKT9PFmQrMz +sVmgIjncklPz4r4e9diWGdHWbBiC + +type: system-user +authority-id: CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez +brand-id: CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez +email: snappy-dev@lists.launchpad.net +models: + - pc +name: user1 +password: $6$o5er943Y$cngsJHutSgACVbR65WAnhaUPC9.vENj8locb50hvMdMRMK8cQ3Zbu6WPh5Al2JrnHzpR63osPCwE/IFG/2s6K1 +series: + - 16 +since: 2020-11-30T11:45:00+00:00 +until: 2030-11-30T11:45:00+00:00 +username: user1 +sign-key-sha3-384: x1Tnl94nkgb-rMDs_l63gvgkLYGjXKfpkCCIQZwJ72-LR6X6OrSvNS9z2WS1lAGz + +AcLBcwQAAQoAHRYhBJABFPajYkcbyArNXdOZThAVY89JBQJfxNykAAoJENOZThAVY89JFOEP/j5+ +sEDuXx2ys/Oy9MsAbW8rje7GkR9G4h1K5H/dgu4EZE76VEiFyWdGIO/Llb9fihBygBNjdVFFs7CS +pihsl/dFQq+p/Q9DklM1g2t0BhhgE5+Kt3ArltWntP6uMFCq0cYzscFHnUNAZVJpHYMOIucq58Ag +LmbKWGpUjF6t1x/fjQ4Z86j6adWvD05G83MreFGVWbIwVf4bvPNwPrMSDN7dMJbi9kyZSbVAlZDe +ciywAr45SzyXjmFX7TA49oIMTrJc2NQrNmfh88VKTRtSRAc7jis8o0laCm95xDhtdqcZp5Q8y/bT +chHAndQZysfOxV8pW3y1GsTp6f3Z8/JoqgjdYfvF29abaCM1ztfaL+XS2Gx+1IGBGSCsswc550fB +YWvE93lReCLL2e/5r7qY83ddp+mvQ3UhZiFcxIzhxHeTDEHsOUBYmJb161CX6LLEiyZ00NZUhrjF +HblVioPYjnNbG2b0CH1mmFSOIn8lhSjDGqIni0tgi9K/LpNe7wzTuyP1yoZCCbM/B/k83mXEBXY8 +hSmDHIKPxGKNXj5cJoOvONG8yjhpB4a/4ec2Lf7R52X4GI/UT1P82eWRmR1/v8++U3Dv1yhPwS/V ++kd8J4nWBiK1BJsAISJsGO9YIVBDHAr5HBIhyVPdl6VAgBFKxfTJ40IphUAKaUwUUsS7exZx diff -Nru snapd-2.48+20.04/tests/lib/assertions/CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez.auto-import.assert.json snapd-2.48.3+20.04/tests/lib/assertions/CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez.auto-import.assert.json --- snapd-2.48+20.04/tests/lib/assertions/CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez.auto-import.assert.json 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/assertions/CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez.auto-import.assert.json 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,13 @@ +{ + "type": "system-user", + "authority-id": "CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez", + "series": ["16"], + "brand-id": "CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez", + "email": "snappy-dev@lists.launchpad.net", + "models": ["pc"], + "name": "user1", + "username": "user1", + "password": "$6$o5er943Y$cngsJHutSgACVbR65WAnhaUPC9.vENj8locb50hvMdMRMK8cQ3Zbu6WPh5Al2JrnHzpR63osPCwE/IFG/2s6K1", + "since": "2020-11-30T11:45:00+00:00", + "until": "2030-11-30T11:45:00+00:00" +} diff -Nru snapd-2.48+20.04/tests/lib/assertions/developer1-20-storage-safety-prefer-unencrypted.json snapd-2.48.3+20.04/tests/lib/assertions/developer1-20-storage-safety-prefer-unencrypted.json --- snapd-2.48+20.04/tests/lib/assertions/developer1-20-storage-safety-prefer-unencrypted.json 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/assertions/developer1-20-storage-safety-prefer-unencrypted.json 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,41 @@ +{ + "type": "model", + "authority-id": "developer1", + "series": "16", + "brand-id": "developer1", + "model": "testkeys-snapd-signed-core-20-amd64", + "architecture": "amd64", + "timestamp": "2020-11-17T22:00:00+00:00", + "grade": "dangerous", + "storage-safety": "prefer-unencrypted", + "base": "core20", + "serial-authority": [ + "generic" + ], + "snaps": [ + { + "default-channel": "20/edge", + "id": "UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH", + "name": "pc", + "type": "gadget" + }, + { + "default-channel": "20/edge", + "id": "pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza", + "name": "pc-kernel", + "type": "kernel" + }, + { + "default-channel": "latest/stable", + "id": "DLqre5XGLbDqg9jPtiAhRRjDuPVa5X1q", + "name": "core20", + "type": "base" + }, + { + "default-channel": "latest/stable", + "id": "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4", + "name": "snapd", + "type": "snapd" + } + ] +} diff -Nru snapd-2.48+20.04/tests/lib/assertions/developer1-20-storage-safety-prefer-unencrypted.model snapd-2.48.3+20.04/tests/lib/assertions/developer1-20-storage-safety-prefer-unencrypted.model --- snapd-2.48+20.04/tests/lib/assertions/developer1-20-storage-safety-prefer-unencrypted.model 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/assertions/developer1-20-storage-safety-prefer-unencrypted.model 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,45 @@ +type: model +authority-id: developer1 +series: 16 +brand-id: developer1 +model: testkeys-snapd-signed-core-20-amd64 +architecture: amd64 +base: core20 +grade: dangerous +serial-authority: + - generic +snaps: + - + default-channel: 20/edge + id: UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH + name: pc + type: gadget + - + default-channel: 20/edge + id: pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza + name: pc-kernel + type: kernel + - + default-channel: latest/stable + id: DLqre5XGLbDqg9jPtiAhRRjDuPVa5X1q + name: core20 + type: base + - + default-channel: latest/stable + id: PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4 + name: snapd + type: snapd +storage-safety: prefer-unencrypted +timestamp: 2020-11-17T22:00:00+00:00 +sign-key-sha3-384: EAD4DbLxK_kn0gzNCXOs3kd6DeMU3f-L6BEsSEuJGBqCORR0gXkdDxMbOm11mRFu + +AcLBUgQAAQoABgUCX7OZLQAA184QAKhwE9DuDhsgyOhAxernDIqHAEOuyI/57qPKDeVWLZLKB9/Y +5BDwR65G3tYL/3ZinnSPmshJYxk/S233JosglguGF+Wykqdhic+FknIy58X1ELVaTvPbn9UgiJBj +5LqUDa/wm0hMTE48UcQuR59qOhTr2sBRjK3ZUfAqHPYGhUgSBa2P2wgAWkRjTI1C58pU/RXgG9Ov +DR8dvZlxPk7G7TwZXr2Q8gT/bEOePiGBD3rE574R8xgRWRYVpEUJQDSTCtS2ZK24DcHt5dO6Y7fj +cxKZae0ySm63TP9pXB5blESj1f9QTLH5lG1NAsKyC3A0yG4s0g2z4LKj23wLuV6GkQmXsLPBI7OT +kUvFq3XxaZnPKF12hYLXc0SLH3pAZruOXtzVW39YwXzCRDZ5g71K19JHjGaKO9PG8YAFC7dNocld +OEFpxFtSIq0SlYXlrDujeQ/LCx89NpD8602By93ZBeYBGVV2WGAE1ccwqDf7yAJ0aWk014c2Ox3h +gqDrMlLdkAyXDMnTVCr9q1Yc+zO3t4ZET/6jb8vdOThX4wfgHUk7RiP3Kfge8CT+piGO9hTDVUdX +b7byA1Ayqgd0/eOc6eKv6uH20BApK9iw1qj/FQFSmnU+cvI1iKfO6dNaReuQkuV3QA4moMQfC7R7 ++BG6unTYovptwY2h5E47bhC76dgj diff -Nru snapd-2.48+20.04/tests/lib/assertions/nested-18-amd64.model snapd-2.48.3+20.04/tests/lib/assertions/nested-18-amd64.model --- snapd-2.48+20.04/tests/lib/assertions/nested-18-amd64.model 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/assertions/nested-18-amd64.model 2021-02-02 08:21:12.000000000 +0000 @@ -1,22 +1,24 @@ type: model -authority-id: BhgbYoDtThegqVkEU7oiZP8GQwCoUIxz +authority-id: CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez series: 16 -brand-id: BhgbYoDtThegqVkEU7oiZP8GQwCoUIxz +brand-id: CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez model: pc architecture: amd64 base: core18 gadget: pc=18 kernel: pc-kernel=18 -timestamp: 2019-05-16T18:06:04+00:00 -sign-key-sha3-384: 0u-IKZyVlTxwiBVOv7oT1FNKdfkKJyOSwbEWJPurlvtaGZxX6VrMHSESnIPeaNMC +serial-authority: + - generic +timestamp: 2020-11-27T15:30:00Z +sign-key-sha3-384: x1Tnl94nkgb-rMDs_l63gvgkLYGjXKfpkCCIQZwJ72-LR6X6OrSvNS9z2WS1lAGz -AcLBcwQAAQoAHRYhBMWWVcwwFvQaEaz9YITG74Zv2rilBQJc3blHAAoJEITG74Zv2rildiMP/1aJ -hXu/kOqDIwatCOgrdRzoesXI2WL4P8cU62wLSITONn4z8TMp4gx5oKnw7HsYEXbNDU2baZBzrUmj -nB4RHtpnhbBBOUs/tx/lGBAUUr0o8+aOl/UMydVxBy8b/IxHABABAKx2/0cr0THv/0ZKdRvTVQLi -PZyKDtHqB6l14OwTWUE5tFQf60BwVXE58OEclAAsrtRvWUVDeL774Tpou2TjXhN1SwEdJPVV1I4i -2fEh3vUZHyuct68W9HODe77XvfRcY5FiEY7G3rd+Xl6DnodCMpjnkPTjcoVgOobny+WGDxtSNxq6 -U4YceYuUguxXfgmGj6QZBvBRdbqVSIfzPif3fe4QvfYeZ1v3paiuuzSPDY5zjd4qbEIwgI/vGm8M -vuletW5IJlJHBErNmSxd7LCkK1ViSCkblNcMit0CdqWJvl74PpbM/mWY/iMTBtA5N9PqPCKh4WC+ -QDYkdnaSxRuZo4zI3lhmJhmcOaehouj5bN3tmUX4zb8jW09hZyQhCGCL480cjwrdf+MtqKb1r076 -H8S8LM43hGNhoArmerXt25/RG8jGaLhqvxwyf+nKGQFc6ZxVVRIfqHA80bmfKuhzh/544VKirFuS -duY/wq6YFprCA4ArSFMHG/3hTKNld5uxGSzCkx2BrddmHkKNVLooLAsYgYvuTjM6Q/Q3F/va +AcLBcwQAAQoAHRYhBJABFPajYkcbyArNXdOZThAVY89JBQJfwRyXAAoJENOZThAVY89JgsMP/jtG +PAG/vdHrg25TAt8T4SSkSW4YGCeOrzn6xddOifRCiBvuc/aYyI4ZAo0E3C5TKDBeDwIyY5ZOuhY0 +C7UNB4xFhJXkmqvkkEg17sOdK9akYijQ0Kb/Kj12KS0xE/8bxkTTbTKiuxxgQZIFB6HpBwU6hYl5 +C6brdurJ+WUsXPUz60NFM6fLirL27i4EOT9iwrdrqFTIKX6tfTZ3j4oWzTdM8OrLA+FkiUFzpxel +3oWGJY2L7ntaFCsaYFTni1oNY5jzSw0xId2twokf4R/n3hqQ6RNA0AkQTVZmEPW9+rszrC+nvLu+ +cQviyhapcp3URZ8E3lFwOognERXOVlebp/0zf6q4Kg/FO/2rBpcu1jGwpo8LdBbL2SOx09uSR0fs +Xpzxk0zul6aVnfC0jB3Jvw5CZPspaxwvtYPVwtMNyXweOYB/0A8NTKhPxNiFhTrXIqW4+Bfa/gLl +2SYkuC6eBcH+na/ENEtBJUVdDvgrVqYXSQOwAwoDhmjM/IBd7xTQZZ9fAHCMjDDpRL8Egq+GMymT +MQ5WowvUebQ9CZWguURaxpzdkiMdcyT7tKUzsLTL+kQeEffvWnLdsshqNfpJo+2m+Z5+kuizlTQ3 +rtIiuPLGm9JKbJPSg0GLX4CizW4K+/Kg9L1DYDQOIErWk5UY21b3f7YYPjx115O7E6JROR06 diff -Nru snapd-2.48+20.04/tests/lib/assertions/nested-18-amd64.model.json snapd-2.48.3+20.04/tests/lib/assertions/nested-18-amd64.model.json --- snapd-2.48+20.04/tests/lib/assertions/nested-18-amd64.model.json 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/assertions/nested-18-amd64.model.json 2021-02-02 08:21:12.000000000 +0000 @@ -1,12 +1,13 @@ { "type": "model", - "authority-id": "BhgbYoDtThegqVkEU7oiZP8GQwCoUIxz", + "authority-id": "CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez", "series": "16", - "brand-id": "BhgbYoDtThegqVkEU7oiZP8GQwCoUIxz", + "brand-id": "CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez", "model": "pc", "architecture": "amd64", "gadget": "pc=18", "base": "core18", "kernel": "pc-kernel=18", - "timestamp": "2019-05-16T18:06:04+00:00" -} \ No newline at end of file + "serial-authority": ["generic"], + "timestamp": "2020-11-27T15:30:00Z" +} diff -Nru snapd-2.48+20.04/tests/lib/assertions/nested-20-amd64.model snapd-2.48.3+20.04/tests/lib/assertions/nested-20-amd64.model --- snapd-2.48+20.04/tests/lib/assertions/nested-20-amd64.model 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/assertions/nested-20-amd64.model 2021-02-02 08:21:12.000000000 +0000 @@ -1,11 +1,13 @@ type: model -authority-id: BhgbYoDtThegqVkEU7oiZP8GQwCoUIxz +authority-id: CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez series: 16 -brand-id: BhgbYoDtThegqVkEU7oiZP8GQwCoUIxz +brand-id: CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez model: pc architecture: amd64 base: core20 grade: dangerous +serial-authority: + - generic snaps: - default-channel: 20/edge @@ -27,16 +29,16 @@ id: PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4 name: snapd type: snapd -timestamp: 2019-05-16T18:06:04+00:00 -sign-key-sha3-384: 0u-IKZyVlTxwiBVOv7oT1FNKdfkKJyOSwbEWJPurlvtaGZxX6VrMHSESnIPeaNMC +timestamp: 2020-11-27T15:30:00Z +sign-key-sha3-384: x1Tnl94nkgb-rMDs_l63gvgkLYGjXKfpkCCIQZwJ72-LR6X6OrSvNS9z2WS1lAGz -AcLBcwQAAQoAHRYhBMWWVcwwFvQaEaz9YITG74Zv2rilBQJecjoSAAoJEITG74Zv2rilrnoQAKGx -xl13XVBJ1iW1k2W9gLJOakMFL3+xgzJU1rHEdk9b0Rcs7+QXqkodA2D8CAVnUnynHcgEWy6y2/DD -j3ffOx/w/I7zDQHOM6jEzEPsAjyhJ/Chei/auj1mLV8sVcdxBxgmPmVSiidCbd0GClU5690wiF81 -Maxn1QoiAt4l3unHN7Jke6SZ1p9uL1XnO0lIRL+QXsfkQ34df+EIxvp+AE5zfEG8N3AGjul73Nwo -iJWX1dFChpcRFcaIBL8ZQoaUGe2HhGrIklQH/EIgIwya4uEop1qSbkF7CIL8yGnkfQKUH03IQa8O -lE5iZJdRwsKV0Z/SfN2Mw2uLlY1jl6HL4wNO6xLXKUXeyx2h3u104fL+i3e0Bin0ERijhnXqZxtv -tA10r6mPBGJ+1/znWS8Gyf7pDIghO6pY0uX7YvwEdo2QRdjHWRQnSsq5iFtHopVH0kkVQTWB80Az -TSEI9y9LMWdUk4dnpGrXffIIgcN9o3bX8t3myvPWZINB+l3RcjCjAEAOAA7FTbwzpdlPO5++hcNY -LMQR2RI+D8GhclR3IhVzQBRf/7SbkenOV6LPjrD9A35c1+2+RoyA/N96QPxV+KJzN3FqDf/TzhTc -LpHr++yZoMgvfmjBH0nlLYPl+P1YUvZA9s9IjjMMcVF5dqSl8BtmlK/Uh7/uYKBSkh9DPqiR +AcLBcwQAAQoAHRYhBJABFPajYkcbyArNXdOZThAVY89JBQJfwRyeAAoJENOZThAVY89JWx4QAMxA +XuEJiU2DAAXa1pl6w7tmBDf7kQ8jMVSNKW6DXAV58yBAUh7oUjS67duLwgaQoF9uPj6qPt5kEONU +s6Mc0DAjQt3bT4KNsfclT/4rN944KNV0m1fDJu7ogZVeGt0xFG8OKV6jVc1SII0A7tkT14rfDeDi +AfeyRMviXQPOjugr8KAR9dG5eHC5yfC1gcN7q89foEH1/l12LQBcyb9GDsNq3Z3mCUMG4l2IrzYy +vAe49QyaagPBMNmPAx+zdRZQd4YX9xhQqwQeYZwsJWGZWx9omWF41EKYMiD1InJwOtTpYDJvCmRc +hkriaBdI3WIL9u39Mm1+ZfZ/BY2RftxBMFOUDJwfzts7Z6ceUsPUXwDa2QiI7mxHMI4luLIUDb0w +Y0pGJR47ZDSrx8ehdpXxYjoNYwRIjwhpzIAUR2tklwFs9gwroq4TOUlx3fBEipAu2QLaVZx9JjTA +oP6oiQ9vX6pw0hsEqsR8PECO+9cjxymr0vslGkucNvcnp16v0ili6oUTkaXDmrtiBjgGLLXXkHJF +jD5ln5TCSqWMbWupqHUa6fxOaBnFlSDMwid3UDxkjAoOaO7McrxYgGYb5miqP4fE4+hZVmP7OVu1 +uzSE7tBqCqXHyUUNcy8d4gN0AlLW+H1EtYD+/PEoODt6FPczIwKrj+DwXOhG+zLmDf992I3J diff -Nru snapd-2.48+20.04/tests/lib/assertions/nested-20-amd64.model.json snapd-2.48.3+20.04/tests/lib/assertions/nested-20-amd64.model.json --- snapd-2.48+20.04/tests/lib/assertions/nested-20-amd64.model.json 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/assertions/nested-20-amd64.model.json 2021-02-02 08:21:12.000000000 +0000 @@ -1,17 +1,18 @@ { "type": "model", - "authority-id": "BhgbYoDtThegqVkEU7oiZP8GQwCoUIxz", + "authority-id": "CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez", "series": "16", - "brand-id": "BhgbYoDtThegqVkEU7oiZP8GQwCoUIxz", + "brand-id": "CA5GLZgNQWPhspDQK63Er46Uxz2SO7ez", "model": "pc", "architecture": "amd64", "base": "core20", "grade": "dangerous", - "timestamp": "2019-05-16T18:06:04+00:00", + "serial-authority": ["generic"], + "timestamp": "2020-11-27T15:30:00Z", "snaps": [ {"default-channel": "20/edge", "id": "UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH", "name": "pc", "type": "gadget"}, {"default-channel": "latest/edge", "id": "DLqre5XGLbDqg9jPtiAhRRjDuPVa5X1q", "name": "core20", "type": "base"}, {"default-channel": "20/edge", "id": "pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza", "name": "pc-kernel", "type": "kernel"}, {"default-channel": "latest/edge", "id": "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4", "name": "snapd", "type": "snapd"} ] -} \ No newline at end of file +} diff -Nru snapd-2.48+20.04/tests/lib/fde-setup-hook/fde-setup.go snapd-2.48.3+20.04/tests/lib/fde-setup-hook/fde-setup.go --- snapd-2.48+20.04/tests/lib/fde-setup-hook/fde-setup.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/fde-setup-hook/fde-setup.go 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,112 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/snapcore/snapd/osutil" +) + +// super secure crypto +func xor13(bs []byte) []byte { + out := make([]byte, len(bs)) + for i := range bs { + out[i] = bs[i] ^ 0x13 + } + return out +} + +// Note that this does not import the snapd structs to ensure we don't +// accidentally break something in the contract and miss that we broke +// it because we use the internal thing "externally" here +type fdeSetupJSON struct { + Op string `json:"op"` + + Key []byte `json:"key,omitempty"` + KeyName string `json:"key-name,omitempty"` + + Models []map[string]string `json:"models,omitempty"` +} + +func runFdeSetup() error { + output, err := exec.Command("snapctl", "fde-setup-request").CombinedOutput() + if err != nil { + return fmt.Errorf("cannot run snapctl fde-setup-request: %v", osutil.OutputErr(output, err)) + } + var js fdeSetupJSON + if err := json.Unmarshal(output, &js); err != nil { + return err + } + + var fdeSetupResult []byte + switch js.Op { + case "features": + // no special features supported by this hook + fdeSetupResult = []byte(`{"features":[]}`) + case "initial-setup": + // "seal" + fdeSetupResult = xor13(js.Key) + default: + return fmt.Errorf("unsupported op %q", js.Op) + } + cmd := exec.Command("snapctl", "fde-setup-result") + cmd.Stdin = bytes.NewBuffer(fdeSetupResult) + output, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("cannot run snapctl fde-setup-result for op %q: %v", js.Op, osutil.OutputErr(output, err)) + } + return nil +} + +type fdeRevealJSON struct { + Op string `json:"op"` + + SealedKey []byte `json:"sealed-key"` +} + +func runFdeRevealKey() error { + var js fdeRevealJSON + + if err := json.NewDecoder(os.Stdin).Decode(&js); err != nil { + return err + } + + switch js.Op { + case "reveal": + // "unseal" + unsealedKey := xor13(js.SealedKey) + fmt.Fprintf(os.Stdout, "%s", unsealedKey) + case "lock": + // nothing right now + case "features": + // XXX: Not used right now but might in the future? + fmt.Fprintf(os.Stdout, `{"features":[]}`) + default: + return fmt.Errorf(`unsupported operations %q`, js.Op) + } + + return nil +} + +func main() { + var err error + + switch filepath.Base(os.Args[0]) { + case "fde-setup": + // run as regular hook + err = runFdeSetup() + case "fde-reveal-key": + // run from initrd + err = runFdeRevealKey() + default: + err = fmt.Errorf("binary needs to be called as fde-setup or fde-reveal-key") + } + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} diff -Nru snapd-2.48+20.04/tests/lib/nested.sh snapd-2.48.3+20.04/tests/lib/nested.sh --- snapd-2.48+20.04/tests/lib/nested.sh 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/nested.sh 2021-02-02 08:21:12.000000000 +0000 @@ -143,8 +143,14 @@ local AUTO_IMPORT_ASSERT if [ -n "$NESTED_CUSTOM_AUTO_IMPORT_ASSERTION" ]; then AUTO_IMPORT_ASSERT=$NESTED_CUSTOM_AUTO_IMPORT_ASSERTION - else - AUTO_IMPORT_ASSERT="$TESTSLIB/assertions/auto-import.assert" + else + local per_model_auto + per_model_auto="$(nested_model_authority).auto-import.assert" + if [ -e "$TESTSLIB/assertions/${per_model_auto}" ]; then + AUTO_IMPORT_ASSERT="$TESTSLIB/assertions/${per_model_auto}" + else + AUTO_IMPORT_ASSERT="$TESTSLIB/assertions/auto-import.assert" + fi fi cp "$AUTO_IMPORT_ASSERT" "$NESTED_ASSETS_DIR/sys-user-partition/auto-import.assert" @@ -422,6 +428,12 @@ esac } +nested_model_authority() { + local model + model="$(nested_get_model)" + grep "authority-id:" "$model"|cut -d ' ' -f2 +} + nested_ensure_ubuntu_save() { local GADGET_DIR="$1" "$TESTSLIB"/ensure_ubuntu_save.py "$GADGET_DIR"/meta/gadget.yaml > /tmp/gadget-with-save.yaml @@ -857,6 +869,10 @@ OVMF_VARS="snakeoil" fi + if [ "${NESTED_ENABLE_OVMF:-}" = "true" ]; then + PARAM_BIOS="-bios /usr/share/OVMF/OVMF_CODE.fd" + fi + if [ "$NESTED_ENABLE_SECURE_BOOT" = "true" ]; then cp -f "/usr/share/OVMF/OVMF_VARS.$OVMF_VARS.fd" "$NESTED_ASSETS_DIR/OVMF_VARS.$OVMF_VARS.fd" PARAM_BIOS="-drive file=/usr/share/OVMF/OVMF_CODE.$OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly -drive file=$NESTED_ASSETS_DIR/OVMF_VARS.$OVMF_VARS.fd,if=pflash,format=raw" diff -Nru snapd-2.48+20.04/tests/lib/prepare.sh snapd-2.48.3+20.04/tests/lib/prepare.sh --- snapd-2.48+20.04/tests/lib/prepare.sh 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/prepare.sh 2021-02-02 08:21:12.000000000 +0000 @@ -512,6 +512,11 @@ echo "echo 'forcibly panicing'; echo c > /proc/sysrq-trigger" >> "$skeletondir/main/usr/lib/the-tool" fi + # copy any extra files to the same location inside the initrd + if [ -d ../extra-initrd/ ]; then + cp -a ../extra-initrd/* "$skeletondir"/main + fi + # XXX: need to be careful to build an initrd using the right kernel # modules from the unpacked initrd, rather than the host which may be # running a different kernel @@ -578,6 +583,11 @@ rm -rf fake ) + # copy any extra files that tests may need for the kernel + if [ -d ./extra-kernel-snap/ ]; then + cp -a ./extra-kernel-snap/* ./repacked-kernel + fi + snap pack repacked-kernel "$TARGET" rm -rf repacked-kernel } diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/bin/sh snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/bin/sh --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/bin/sh 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/bin/sh 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +PS1='$ ' +exec /bin/sh "$@" diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/meta/snap.yaml snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/meta/snap.yaml --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/meta/snap.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/meta/snap.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,31 @@ +name: test-snapd-complex-layout +version: 1.0 +apps: + sh: + command: bin/sh + +# taken from https://bugs.launchpad.net/snapd/+bug/1906821 +layout: + $SNAP/config: + bind: $SNAP_DATA/config + $SNAP/certs: + bind: $SNAP_DATA/certs + /usr/bin/python3.8: + symlink: $SNAP/usr/bin/python3.8 + /usr/bin/python3.7: + # python3.7 lambdas will be redirected to python3.8 + symlink: $SNAP/usr/bin/python3.8 + $SNAP/usr/bin/python3: + symlink: $SNAP/usr/bin/python3.8 + /usr/bin/python2.7: + symlink: $SNAP/usr/bin/python2.7 + $SNAP/usr/bin/python: + symlink: $SNAP/usr/bin/python2.7 + /usr/bin/nodejs12.x: + symlink: $SNAP/wrapper-scripts/exec-node.sh + /usr/bin/node: + symlink: $SNAP/node-v12.18.4-linux/bin/node + /usr/bin/java8: + symlink: $SNAP/usr/lib/jvm/java-8-openjdk-arch-symlink/jre/bin/java + /usr/bin/java: + symlink: $SNAP/usr/lib/jvm/java-8-openjdk-arch-symlink/jre/bin/java diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/node/bin/node snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/node/bin/node --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/node/bin/node 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/node/bin/node 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1 @@ +dummy diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/bin/python2.7 snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/bin/python2.7 --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/bin/python2.7 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/bin/python2.7 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1 @@ +dummy diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/bin/python3.8 snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/bin/python3.8 --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/bin/python3.8 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/bin/python3.8 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1 @@ +dummy diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/lib/jvm/jre/bin/java snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/lib/jvm/jre/bin/java --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/lib/jvm/jre/bin/java 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/usr/lib/jvm/jre/bin/java 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1 @@ +dummy diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/wrapper-scripts/exec-node.sh snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/wrapper-scripts/exec-node.sh --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-complex-layout/wrapper-scripts/exec-node.sh 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-complex-layout/wrapper-scripts/exec-node.sh 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1 @@ +dummy diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-sh/bin/cmd snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-sh/bin/cmd --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-sh/bin/cmd 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-sh/bin/cmd 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,6 @@ +#!/bin/sh +PS1='$ ' +command="$1" +shift + +exec "$command" "$@" diff -Nru snapd-2.48+20.04/tests/lib/snaps/test-snapd-sh/meta/snap.yaml snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-sh/meta/snap.yaml --- snapd-2.48+20.04/tests/lib/snaps/test-snapd-sh/meta/snap.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/snaps/test-snapd-sh/meta/snap.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -5,3 +5,5 @@ apps: sh: command: bin/sh + cmd: + command: bin/cmd diff -Nru snapd-2.48+20.04/tests/lib/store.sh snapd-2.48.3+20.04/tests/lib/store.sh --- snapd-2.48+20.04/tests/lib/store.sh 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/lib/store.sh 2021-02-02 08:21:12.000000000 +0000 @@ -147,6 +147,9 @@ teardown_fake_store(){ local top_dir=$1 systemctl stop fakestore || true + # when a unit fails, systemd may keep its status, resetting it allows to + # start the unit again with a clean slate + systemctl reset-failed fakestore || true if [ "$REMOTE_STORE" = "staging" ]; then setup_staging_store diff -Nru snapd-2.48+20.04/tests/main/cloud-init/task.yaml snapd-2.48.3+20.04/tests/main/cloud-init/task.yaml --- snapd-2.48+20.04/tests/main/cloud-init/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/main/cloud-init/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -84,4 +84,6 @@ # checking that it was restricted? echo "Test that cloud-init restrict file was written" test -f /etc/cloud/cloud.cfg.d/zzzz_snapd.cfg + echo "Test that cloud-init restrict file does NOT have manual_cache_clean set" + NOMATCH "manual_cache_clean: true" < /etc/cloud/cloud.cfg.d/zzzz_snapd.cfg fi diff -Nru snapd-2.48+20.04/tests/main/cohorts/task.yaml snapd-2.48.3+20.04/tests/main/cohorts/task.yaml --- snapd-2.48+20.04/tests/main/cohorts/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/main/cohorts/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -1,13 +1,19 @@ summary: Check that cohorts work prepare: | - snap install yq - snap connect yq:home + "$TESTSTOOLS"/snaps-state install-local test-snapd-sh + +debug: | + cat coh.yml || true execute: | echo "Test we can create chorts:" snap create-cohort test-snapd-tools > coh.yml - COHORT=$( yq r coh.yml cohorts.test-snapd-tools.cohort-key ) + # the YAML looks like this: + # cohorts: + # test-snapd-tools: + # cohort-key: + COHORT=$(test-snapd-sh.cmd python3 -c 'import sys, yaml; print(yaml.safe_load(sys.stdin)["cohorts"]["test-snapd-tools"]["cohort-key"])' < coh.yml) test -n "$COHORT" echo "Test we can install from there:" diff -Nru snapd-2.48+20.04/tests/main/fake-netplan-apply/task.yaml snapd-2.48.3+20.04/tests/main/fake-netplan-apply/task.yaml --- snapd-2.48+20.04/tests/main/fake-netplan-apply/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/main/fake-netplan-apply/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -18,6 +18,12 @@ - -arch-* - -amazon-* - -centos-* + # TODO: re-enable this when network-setup-control is updated to allow + # running netplan directly from the base snap and we can update this test to + # just using a simple shell script snap instead of building netplan from + # source, as currently that is broken on xenial with + # test-snapd-netplan-apply for some reason, see https://github.com/anonymouse64/test-snapd-netplan-apply/issues/1 + - -ubuntu-16* prepare: | #shellcheck source=tests/lib/snaps.sh diff -Nru snapd-2.48+20.04/tests/main/uc20-create-partitions/task.yaml snapd-2.48.3+20.04/tests/main/uc20-create-partitions/task.yaml --- snapd-2.48+20.04/tests/main/uc20-create-partitions/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/main/uc20-create-partitions/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -18,6 +18,7 @@ losetup -d "$LOOP" umount "${LOOP}p3" umount "${LOOP}p4" + umount "${LOOP}p5" fi prepare: | @@ -59,11 +60,13 @@ sfdisk -l "$LOOP" | MATCH '750M Linux filesystem' sfdisk -l "$LOOP" | MATCH '16.7G Linux filesystem' file -s "${LOOP}p3" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-boot"' - file -s "${LOOP}p4" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-data"' + file -s "${LOOP}p4" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-save"' + file -s "${LOOP}p5" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-data"' echo "Check that the filesystems were not auto-mounted" mount | not MATCH /run/mnt/ubuntu-seed mount | not MATCH /run/mnt/ubuntu-boot + mount | not MATCH /run/mnt/ubuntu-save mount | not MATCH /run/mnt/ubuntu-data # we used "lsblk --fs" here but it was unreliable @@ -84,11 +87,19 @@ mount "${LOOP}p4" ./mnt df -T "${LOOP}p4" | MATCH ext4 umount ./mnt - file -s "${LOOP}p4" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-data"' + file -s "${LOOP}p4" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-save"' # check metadata_csum - tune2fs -l "${LOOP}p3" | MATCH '^Filesystem features:.*metadata_csum' + tune2fs -l "${LOOP}p4" | MATCH '^Filesystem features:.*metadata_csum' + + mkdir -p ./mnt + mount "${LOOP}p5" ./mnt + df -T "${LOOP}p5" | MATCH ext4 + umount ./mnt + file -s "${LOOP}p5" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-data"' + # check metadata_csum + tune2fs -l "${LOOP}p5" | MATCH '^Filesystem features:.*metadata_csum' # size is reported in 512 blocks - sz="$(udevadm info -q property "${LOOP}p4" |grep "^ID_PART_ENTRY_SIZE=" | cut -f2 -d=)" + sz="$(udevadm info -q property "${LOOP}p5" |grep "^ID_PART_ENTRY_SIZE=" | cut -f2 -d=)" # the disk size is 20GB, 1GB in 512 blocks is 2097152, with auto grow, the # partition can be safely assumed to be > 10GB if [ "$sz" -lt "$((10 * 2097152))" ]; then @@ -117,17 +128,19 @@ sfdisk -l "$LOOP" | MATCH "${LOOP}p1 .* 1M\s* BIOS boot" sfdisk -l "$LOOP" | MATCH "${LOOP}p2 .* 1.2G\s* EFI System" sfdisk -l "$LOOP" | MATCH "${LOOP}p3 .* 750M\s* Linux filesystem" - sfdisk -l "$LOOP" | MATCH "${LOOP}p4 .* 1G\s* Linux filesystem" - sfdisk -l "$LOOP" | MATCH "${LOOP}p5 .* 110M\s* Linux filesystem" + sfdisk -l "$LOOP" | MATCH "${LOOP}p4 .* 16M\s* Linux filesystem" + sfdisk -l "$LOOP" | MATCH "${LOOP}p5 .* 1G\s* Linux filesystem" + sfdisk -l "$LOOP" | MATCH "${LOOP}p6 .* 110M\s* Linux filesystem" echo "check that the filesystems are created and mounted" mount mount | MATCH /run/mnt/ubuntu-boot + mount | MATCH /run/mnt/ubuntu-save mount | MATCH /run/mnt/ubuntu-data mount | MATCH /run/mnt/other-ext4 - df -T "${LOOP}p5" | MATCH ext4 - file -s "${LOOP}p5" | MATCH 'volume name "other-ext4"' + df -T "${LOOP}p6" | MATCH ext4 + file -s "${LOOP}p6" | MATCH 'volume name "other-ext4"' umount /run/mnt/other-ext4 echo "Make sure the filesystem was redeployed" diff -Nru snapd-2.48+20.04/tests/main/uc20-create-partitions-encrypt/task.yaml snapd-2.48.3+20.04/tests/main/uc20-create-partitions-encrypt/task.yaml --- snapd-2.48+20.04/tests/main/uc20-create-partitions-encrypt/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/main/uc20-create-partitions-encrypt/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -17,9 +17,11 @@ umount ./mnt || true fi umount /run/mnt/ubuntu-seed || true + umount /dev/mapper/ubuntu-save || true umount /dev/mapper/ubuntu-data || true umount /dev/mapper/test-udata || true + cryptsetup close /dev/mapper/ubuntu-save || true cryptsetup close /dev/mapper/ubuntu-data || true cryptsetup close /dev/mapper/test-udata || true @@ -107,14 +109,19 @@ sfdisk -d "$LOOP" | MATCH "^${LOOP}p1 .*size=\s*2048, type=21686148-6449-6E6F-744E-656564454649,.*BIOS Boot" sfdisk -d "$LOOP" | MATCH "^${LOOP}p2 .*size=\s*2457600, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B,.*ubuntu-seed" sfdisk -d "$LOOP" | MATCH "^${LOOP}p3 .*size=\s*1536000, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4,.*ubuntu-boot" - sfdisk -d "$LOOP" | MATCH "^${LOOP}p4 .*size=\s*15533521, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4,.*ubuntu-data" + sfdisk -d "$LOOP" | MATCH "^${LOOP}p4 .*size=\s*32768, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4,.*ubuntu-save" + sfdisk -d "$LOOP" | MATCH "^${LOOP}p5 .*size=\s*15500753, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4,.*ubuntu-data" not cryptsetup isLuks "${LOOP}p1" not cryptsetup isLuks "${LOOP}p2" not cryptsetup isLuks "${LOOP}p3" cryptsetup isLuks "${LOOP}p4" + cryptsetup isLuks "${LOOP}p5" - cryptsetup luksDump "${LOOP}p4" | MATCH 'Label:\s*ubuntu-data-enc' + cryptsetup luksDump "${LOOP}p4" | MATCH 'Label:\s*ubuntu-save-enc' + POSIXLY_CORRECT=1 file -s /dev/mapper/ubuntu-save | MATCH 'volume name "ubuntu-save"' + + cryptsetup luksDump "${LOOP}p5" | MATCH 'Label:\s*ubuntu-data-enc' POSIXLY_CORRECT=1 file -s /dev/mapper/ubuntu-data | MATCH 'volume name "ubuntu-data"' cryptsetup close /dev/mapper/ubuntu-data @@ -123,14 +130,14 @@ # Test the unsealed key echo "Ensure that we can open the encrypted device using the unsealed key" - cryptsetup open --key-file unsealed-key "${LOOP}p4" test + cryptsetup open --key-file unsealed-key "${LOOP}p5" test mount /dev/mapper/test ./mnt umount ./mnt cryptsetup close /dev/mapper/test # Test the recovery key echo "Ensure that we can open the encrypted device using the recovery key" - cryptsetup open --key-file recovery-key "${LOOP}p4" test-recovery + cryptsetup open --key-file recovery-key "${LOOP}p5" test-recovery mount /dev/mapper/test-recovery ./mnt umount ./mnt cryptsetup close /dev/mapper/test-recovery diff -Nru snapd-2.48+20.04/tests/main/uc20-create-partitions-reinstall/task.yaml snapd-2.48.3+20.04/tests/main/uc20-create-partitions-reinstall/task.yaml --- snapd-2.48+20.04/tests/main/uc20-create-partitions-reinstall/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/main/uc20-create-partitions-reinstall/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -18,6 +18,7 @@ fi umount ./ubuntu-seed || true umount ./ubuntu-boot || true + umount ./ubuntu-save || true umount ./ubuntu-data || true prepare: | @@ -62,14 +63,16 @@ sfdisk -l "$LOOP" | MATCH '750M Linux filesystem' sfdisk -l "$LOOP" | MATCH '16\.7G Linux filesystem' file -s "${LOOP}p3" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-boot"' - file -s "${LOOP}p4" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-data"' + file -s "${LOOP}p4" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-save"' + file -s "${LOOP}p5" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-data"' echo "Create canary files on the ubuntu-{seed,boot,data} partitions" - mkdir ./ubuntu-seed ./ubuntu-boot ./ubuntu-data + mkdir ./ubuntu-seed ./ubuntu-boot ./ubuntu-save ./ubuntu-data mount "${LOOP}p2" ./ubuntu-seed mount "${LOOP}p3" ./ubuntu-boot - mount "${LOOP}p4" ./ubuntu-data - for label in ubuntu-seed ubuntu-boot ubuntu-data; do + mount "${LOOP}p4" ./ubuntu-save + mount "${LOOP}p5" ./ubuntu-data + for label in ubuntu-seed ubuntu-boot ubuntu-save ubuntu-data; do echo "$label" > ./"$label"/canary.txt umount ./"$label" done @@ -81,16 +84,19 @@ echo "And check that the partitions are there" sfdisk -l "$LOOP" | MATCH '750M Linux filesystem' sfdisk -l "$LOOP" | MATCH '16\.7G Linux filesystem' - sfdisk -l "$LOOP" | not MATCH "${LOOP}p[56789]" + sfdisk -l "$LOOP" | NOMATCH "${LOOP}p[6789]" file -s "${LOOP}p3" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-boot"' - file -s "${LOOP}p4" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-data"' + file -s "${LOOP}p4" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-save"' + file -s "${LOOP}p5" | MATCH 'ext4 filesystem data,.* volume name "ubuntu-data"' echo "Mount partitions again" mount "${LOOP}p2" ./ubuntu-seed mount "${LOOP}p3" ./ubuntu-boot - mount "${LOOP}p4" ./ubuntu-data + mount "${LOOP}p4" ./ubuntu-save + mount "${LOOP}p5" ./ubuntu-data echo "The ubuntu-seed partition is still there untouched" test -e ./ubuntu-seed/canary.txt - echo "But ubuntu-{boot,data} got re-created" + echo "But ubuntu-{boot,save,data} got re-created" not test -e ./ubuntu-boot/canary.txt + not test -e ./ubuntu-save/canary.txt not test -e ./ubuntu-data/canary.txt diff -Nru snapd-2.48+20.04/tests/nested/core20/basic/task.yaml snapd-2.48.3+20.04/tests/nested/core20/basic/task.yaml --- snapd-2.48+20.04/tests/nested/core20/basic/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/nested/core20/basic/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -40,3 +40,11 @@ echo "snap recovery --show-key should not work as a user" exit 1 fi + + echo "Wait for device initialisation to be done" + while ! nested_exec snap changes | grep -q "Done.*Initialize device"; do sleep 1; done + + echo "Check that the serial backed up to save is as expected" + nested_exec 'cat /var/lib/snapd/save/device/asserts-v0/serial/'"$(nested_model_authority)"'/pc/*/active' >serial.saved + nested_exec snap model --serial --assertion >serial + cmp serial serial.saved diff -Nru snapd-2.48+20.04/tests/nested/manual/cloud-init-nocloud-not-vuln/task.yaml snapd-2.48.3+20.04/tests/nested/manual/cloud-init-nocloud-not-vuln/task.yaml --- snapd-2.48+20.04/tests/nested/manual/cloud-init-nocloud-not-vuln/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/nested/manual/cloud-init-nocloud-not-vuln/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -112,6 +112,7 @@ nested_exec "cloud-init status" | MATCH "status: done" nested_exec "test ! -f /etc/cloud/cloud-init.disabled" nested_exec "test -f /etc/cloud/cloud.cfg.d/zzzz_snapd.cfg" + nested_exec "cat /etc/cloud/cloud.cfg.d/zzzz_snapd.cfg" | MATCH "manual_cache_clean: true" # save snapd logs before continuing as the logs are not persistent nested_exec "sudo journalctl -e --no-pager -u snapd" > snapd-before-reboot.logs diff -Nru snapd-2.48+20.04/tests/nested/manual/core20-early-config/task.yaml snapd-2.48.3+20.04/tests/nested/manual/core20-early-config/task.yaml --- snapd-2.48+20.04/tests/nested/manual/core20-early-config/task.yaml 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/tests/nested/manual/core20-early-config/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -76,10 +76,8 @@ # now transition to recover mode and check it again boot_id="$(nested_get_boot_id)" - # TODO:UC20: shouldn't need to specify the label here, fix when snap reboot - # works # shellcheck disable=SC2016 - nested_exec 'sudo snap reboot --recover $(sudo snap recovery | grep -v Label | awk "{print \$1}")' + nested_exec 'sudo snap reboot --recover' nested_wait_for_reboot "${boot_id}" # verify in recover mode diff -Nru snapd-2.48+20.04/tests/nested/manual/uc20-fde-hooks/task.yaml snapd-2.48.3+20.04/tests/nested/manual/uc20-fde-hooks/task.yaml --- snapd-2.48+20.04/tests/nested/manual/uc20-fde-hooks/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/nested/manual/uc20-fde-hooks/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,42 @@ +summary: Check that the fde-setup hooks work + +# this is a UC20 specific test +systems: [ubuntu-20.04-64] + +environment: + NESTED_IMAGE_ID: core20-fde-setup + NESTED_ENABLE_TPM: false + NESTED_ENABLE_SECURE_BOOT: false + NESTED_BUILD_SNAPD_FROM_CURRENT: true + NESTED_ENABLE_OVMF: true + GO111MODULE: off + +prepare: | + echo "Build a kernel snap with the fde-setup hook" + # shellcheck source=tests/lib/prepare.sh + . "$TESTSLIB/prepare.sh" + # shellcheck source=tests/lib/nested.sh + . "$TESTSLIB/nested.sh" + mkdir ./extra-snaps + # build fde-reveal-key hook into the "extra-initrd" dir so that the + # nested_create_core_vm picks this up + mkdir -p ./extra-initrd/usr/bin/ + go build -o ./extra-initrd/usr/bin/fde-reveal-key "$TESTSLIB"/fde-setup-hook/fde-setup.go + + # create fde-setup hook inside the kernel + mkdir -p ./extra-kernel-snap/meta/hooks + go build -o ./extra-kernel-snap/meta/hooks/fde-setup "$TESTSLIB"/fde-setup-hook/fde-setup.go + nested_create_core_vm + # start it + nested_start_core_vm + +execute: | + # shellcheck source=tests/lib/nested.sh + . "$TESTSLIB/nested.sh" + + # check that we have an encrypted system + nested_exec "find /dev/mapper" | MATCH ubuntu-data-[0-9a-f-]+ + nested_exec "test -e /var/lib/snapd/device/fde/recovery.key" + nested_exec "test -e /run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key" + nested_exec "test -e /run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key" + nested_exec "test -e /run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key" diff -Nru snapd-2.48+20.04/tests/nested/manual/uc20-storage-safety/task.yaml snapd-2.48.3+20.04/tests/nested/manual/uc20-storage-safety/task.yaml --- snapd-2.48+20.04/tests/nested/manual/uc20-storage-safety/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/nested/manual/uc20-storage-safety/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,71 @@ +summary: Test that storage-safety prefer-unencrypted works + +systems: [ubuntu-20.04-64] + +environment: + NESTED_ENABLE_TPM: true + NESTED_ENABLE_SECURE_BOOT: true + NESTED_BUILD_SNAPD_FROM_CURRENT: true + NESTED_USE_CLOUD_INIT: true + + MODEL_STORAGE_SAFETY/preferunencrypted: prefer-unencrypted + NESTED_CUSTOM_MODEL: $TESTSLIB/assertions/developer1-20-storage-safety-${MODEL_STORAGE_SAFETY}.model + NESTED_IMAGE_ID: core20-storage-safety-${MODEL_STORAGE_SAFETY} + NESTED_UBUNTU_IMAGE_SNAPPY_FORCE_SAS_URL: http://localhost:11028 + NESTED_FAKESTORE_BLOB_DIR: $(pwd)/fake-store-blobdir + +prepare: | + if [ "$TRUST_TEST_KEYS" = "false" ]; then + echo "This test needs test keys to be trusted" + exit + fi + + #shellcheck source=tests/lib/nested.sh + . "$TESTSLIB/nested.sh" + + #shellcheck source=tests/lib/store.sh + . "$TESTSLIB"/store.sh + + # setup the fakestore, but don't use it for our snapd here on the + # host VM, so tear down the staging_store immediately afterwards + # so that only the SAS is running and our snapd is not pointed at + # it, ubuntu-image is the only thing that actually needs to use + # the fakestore, and we will manually point it at the fakestore + # below using NESTED_UBUNTU_IMAGE_SNAPPY_FORCE_SAS_URL + setup_fake_store "$NESTED_FAKESTORE_BLOB_DIR" + teardown_staging_store + + echo Expose the needed assertions through the fakestore + cp "$TESTSLIB"/assertions/developer1.account "$NESTED_FAKESTORE_BLOB_DIR/asserts" + cp "$TESTSLIB"/assertions/developer1.account-key "$NESTED_FAKESTORE_BLOB_DIR/asserts" + + "$TESTSTOOLS"/nested-state build-image core + "$TESTSTOOLS"/nested-state create-vm core + +restore: | + if [ "$TRUST_TEST_KEYS" = "false" ]; then + echo "This test needs test keys to be trusted" + exit + fi + + #shellcheck source=tests/lib/store.sh + . "$TESTSLIB"/store.sh + teardown_fake_store "$NESTED_FAKESTORE_BLOB_DIR" + +execute: | + if [ "$TRUST_TEST_KEYS" = "false" ]; then + echo "This test needs test keys to be trusted" + exit + fi + + #shellcheck source=tests/lib/nested.sh + . "$TESTSLIB/nested.sh" + + echo "Verify that no fde keys are generated" + nested_exec "test ! -d /var/lib/snapd/device/fde" + + # TODO: once we have a install-mode log (PR#9545) we could grep + # "installing system unencrypted because of ..." + + # check that data is mounted from a regular device, not a mapper + nested_exec "mount" | MATCH "/dev/[svh]da[45] on /run/mnt/data" diff -Nru snapd-2.48+20.04/tests/regression/lp-1906821/task.yaml snapd-2.48.3+20.04/tests/regression/lp-1906821/task.yaml --- snapd-2.48+20.04/tests/regression/lp-1906821/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/regression/lp-1906821/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,37 @@ +summary: verify that parallel instances of snaps with complex layout are correct + +description: | + The layout in https://bugs.launchpad.net/snapd/+bug/1906821 would be broken + with parallel instances, as due to a bug in sorting of mount entries, the + mapping of /var/snap/aws-iot-greengrass_prime to /var/snap/aws-iot-greengrass + was established too late. Layout entries referring to $SNAP_DATA would access + content from real /var/snap/aws-iot-greengrass instead of the instance one. + +# it's enough to run it on one system +systems: [ubuntu-20.04-64] + +prepare: | + echo "Ensure feature flags are enabled" + snap set system experimental.parallel-instances=true + +execute: | + echo "Install snaps" + "$TESTSTOOLS"/snaps-state install-local test-snapd-complex-layout + "$TESTSTOOLS"/snaps-state install-local-as test-snapd-complex-layout test-snapd-complex-layout_prime + + mkdir -p /var/snap/test-snapd-complex-layout/current/certs + mkdir -p /var/snap/test-snapd-complex-layout_prime/current/certs + # this file should only be visible in test-snapd-complex-layout snap mount + # namespace + touch /var/snap/test-snapd-complex-layout/current/certs/non-instance + # this file should only be visible in test-snapd-complex-layout_prime mount + # namespace + touch /var/snap/test-snapd-complex-layout_prime/current/certs/prime-instance + #shellcheck disable=SC2016 + test-snapd-complex-layout.sh -c 'test -e $SNAP/certs/non-instance' + #shellcheck disable=SC2016 + not test-snapd-complex-layout.sh -c 'test -e $SNAP/certs/prime-instance' + #shellcheck disable=SC2016 + test-snapd-complex-layout_prime.sh -c 'test -e $SNAP/certs/prime-instance' + #shellcheck disable=SC2016 + not test-snapd-complex-layout_prime.sh -c 'test -e $SNAP/certs/non-instance' diff -Nru snapd-2.48+20.04/tests/regression/lp-1910456/container-mgr-snap/bin/simple.sh snapd-2.48.3+20.04/tests/regression/lp-1910456/container-mgr-snap/bin/simple.sh --- snapd-2.48+20.04/tests/regression/lp-1910456/container-mgr-snap/bin/simple.sh 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/regression/lp-1910456/container-mgr-snap/bin/simple.sh 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff -Nru snapd-2.48+20.04/tests/regression/lp-1910456/container-mgr-snap/meta/snap.yaml snapd-2.48.3+20.04/tests/regression/lp-1910456/container-mgr-snap/meta/snap.yaml --- snapd-2.48+20.04/tests/regression/lp-1910456/container-mgr-snap/meta/snap.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/regression/lp-1910456/container-mgr-snap/meta/snap.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,91 @@ +name: test-snapd-container-mgrs +summary: A test snap that uses super-privileged container mgr interfaces +version: 1.0 + +plugs: + greengrass-no-cont: + interface: greengrass-support + flavor: no-container + greengrass-legacy-cont: + interface: greengrass-support + flavor: legacy-container + kubelet: + interface: kubernetes-support + flavor: kubelet + kubeproxy: + interface: kubernetes-support + flavor: kubeproxy + autobind-unix: + interface: kubernetes-support + flavor: autobind-unix + +apps: + docker-support: + command: bin/simple.sh + daemon: simple + plugs: + - docker-support + + greengrass-support: + command: bin/simple.sh + daemon: simple + plugs: + - greengrass-support + + kubernetes-support: + command: bin/simple.sh + daemon: simple + plugs: + - kubernetes-support + + lxd-support: + command: bin/simple.sh + daemon: simple + plugs: + - lxd-support + + greengrass-no-cont: + command: bin/simple.sh + daemon: simple + plugs: + - greengrass-no-cont + + greengrass-legacy-cont: + command: bin/simple.sh + daemon: simple + plugs: + - greengrass-legacy-cont + + kubelet: + command: bin/simple.sh + daemon: simple + plugs: + - kubelet + + kubeproxy: + command: bin/simple.sh + daemon: simple + plugs: + - kubeproxy + + autobind-unix: + command: bin/simple.sh + daemon: simple + plugs: + - autobind-unix + + # service without plugs to make sure it doesn't get the Delegate=true + bare: + command: bin/simple.sh + daemon: simple + + # service with multiple plugs to make sure it doesn't have multiple + # Delegate=true settings in the service unit + multiple-plugs: + command: bin/simple.sh + daemon: simple + plugs: + - docker-support + - lxd-support + - kubernetes-support + - greengrass-support diff -Nru snapd-2.48+20.04/tests/regression/lp-1910456/task.yaml snapd-2.48.3+20.04/tests/regression/lp-1910456/task.yaml --- snapd-2.48+20.04/tests/regression/lp-1910456/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.48.3+20.04/tests/regression/lp-1910456/task.yaml 2021-02-02 08:21:12.000000000 +0000 @@ -0,0 +1,143 @@ +summary: container management snaps should have special systemd unit directives +details: | + Some special interfaces have additional systemd unit directives added to + their systemd units, specifically container management interfaces have the + Delegate=true snippet added to prevent CVE-2020-27352. + +prepare: | + # build and install the strict test snap + snap pack container-mgr-snap + snap install --dangerous test-snapd-container-mgrs*.snap + + if os.query is-classic; then + # then also check the classic snap too + + # create the classic test snap by just adding "confinement: classic" to the + # snap.yaml of the strict snap + + cp -r container-mgr-snap classic-container-mgr-snap + sed -i -e "s@name: test-snapd-container-mgrs@name: test-snapd-classic-container-mgrs@" \ + classic-container-mgr-snap/meta/snap.yaml + echo "confinement: classic" >> classic-container-mgr-snap/meta/snap.yaml + + # TODO: use os.query is-arch on master; "is-arch" etc. sub-commands are + # unavailable on 2.48 branch + if [ "$SPREAD_SYSTEM" = "arch-linux-64" ] || [[ "$SPREAD_SYSTEM" == fedora-* ]] || [[ "$SPREAD_SYSTEM" == centos-* ]]; then + # need to enable /snap symlink to install classic snaps + ln -s /var/lib/snapd/snap /snap + fi + + snap pack classic-container-mgr-snap + snap install --dangerous --classic test-snapd-classic-container-mgrs*.snap + fi + +execute: | + for confinement in classic strict; do + echo "Check that all services exist in the $confinement snap" + + if [ "$confinement" = "classic" ]; then + if os.query is-core; then + # skip the classic snap version of the test on core systems + echo "Skipping classic variant on Ubuntu Core" + continue + fi + + snapName="test-snapd-classic-container-mgrs" + else + snapName="test-snapd-container-mgrs" + fi + + snap services | MATCH $snapName\.docker-support + snap services | MATCH $snapName\.greengrass-support + snap services | MATCH $snapName\.kubernetes-support + snap services | MATCH $snapName\.lxd-support + + snap services | MATCH $snapName\.greengrass-no-cont + snap services | MATCH $snapName\.greengrass-legacy-cont + + snap services | MATCH $snapName\.kubelet + snap services | MATCH $snapName\.kubeproxy + snap services | MATCH $snapName\.autobind-unix + + snap services | MATCH $snapName\.bare + + snap services | MATCH $snapName\.multiple-plugs + + echo "Check that the container services have Delegate=true in their service unit exactly once" + + for svc in docker-support greengrass-support kubernetes-support lxd-support greengrass-legacy-cont kubelet kubeproxy multiple-plugs; do + + serviceFile="/etc/systemd/system/snap.$snapName.$svc.service" + + cat "$serviceFile" | MATCH "Delegate=true" + test "$(cat "$serviceFile" | grep "Delegate=true" | wc -l)" = 1 + + # connect and disconnect the interfaces to ensure that + # connection/disconnection does not effect the presence of Delegate=true + + if [ "$svc" = "multiple-plugs" ]; then + snap connect "$snapName:docker-support" + snap connect "$snapName:greengrass-support" + snap connect "$snapName:kubernetes-support" + snap connect "$snapName:lxd-support" + else + snap connect "$snapName:$svc" + fi + + cat "$serviceFile" | MATCH "Delegate=true" + test "$(cat "$serviceFile" | grep "Delegate=true" | wc -l)" = 1 + + if [ "$svc" = "multiple-plugs" ]; then + snap disconnect "$snapName:docker-support" + snap disconnect "$snapName:greengrass-support" + snap disconnect "$snapName:kubernetes-support" + snap disconnect "$snapName:lxd-support" + else + snap disconnect "$snapName:$svc" + fi + + cat "$serviceFile" | MATCH "Delegate=true" + test "$(cat "$serviceFile" | grep "Delegate=true" | wc -l)" = 1 + done + + echo "Check that the non-container manager services do not have Delegate=true in their service units" + + for svc in bare "autobind-unix" "greengrass-no-cont"; do + serviceFile="/etc/systemd/system/snap.$snapName.$svc.service" + cat "$serviceFile" | NOMATCH "Delegate=true" + done + done + + # only run this test on ubuntu since the docker snap is not guaranteed to + # work on non-ubuntu systems + + if ! [[ "$SPREAD_SYSTEM" == ubuntu-* ]]; then + echo "skipping docker test on non-ubuntu" + exit 0 + fi + + snap install docker + + echo "Test that docker container cgroups on ubuntu are not moved when systemctl daemon-reload is executed" + + # start a docker container - need to wait until dockerd comes alive + retry -n10 --wait 1 sh -c 'docker run -d --name test ubuntu:18.04 sleep infinity' + + # make sure that docker top can see the process + docker top test | MATCH "sleep infinity" + + # get the pid of that docker container + containerPID=$(docker top test | grep "sleep infinity" | awk '{print $2}') + + containerCgroupBefore=$(cat "/proc/$containerPID/cgroup" | grep devices) + + # reload systemd + systemctl daemon-reload + + # check the container cgroup again + containerCgroupAfter=$(cat "/proc/$containerPID/cgroup" | grep devices) + + if [ "$containerCgroupBefore" != "$containerCgroupAfter" ]; then + echo "test broken, container was moved from cgroup \"$containerCgroupBefore\" to \"$containerCgroupAfter\"" + exit 1 + fi diff -Nru snapd-2.48+20.04/update-pot snapd-2.48.3+20.04/update-pot --- snapd-2.48+20.04/update-pot 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/update-pot 2021-02-02 08:21:12.000000000 +0000 @@ -12,8 +12,9 @@ c1="Alternative command to run" c2="Name of the key to use, otherwise use the default key" c3="too many arguments for command" + c4="%d days ago, at 15:04 MST" - for canary in "$c1" "$c2" "$c3"; do + for canary in "$c1" "$c2" "$c3" "$c4"; do if ! grep -q "$canary" "$OUTPUT"; then echo "canary '$canary' not found, pot extraction broken" ls -lh "$OUTPUT" @@ -47,7 +48,7 @@ --package-name=snappy\ --msgid-bugs-address=snappy-devel@lists.ubuntu.com \ --keyword=i18n.G \ - --keyword-plural=i18n.DG + --keyword-plural=i18n.NG # check canary check_canaries diff -Nru snapd-2.48+20.04/vendor/github.com/snapcore/secboot/secureboot_policy.go snapd-2.48.3+20.04/vendor/github.com/snapcore/secboot/secureboot_policy.go --- snapd-2.48+20.04/vendor/github.com/snapcore/secboot/secureboot_policy.go 2020-11-18 09:44:21.000000000 +0000 +++ snapd-2.48.3+20.04/vendor/github.com/snapcore/secboot/secureboot_policy.go 2021-02-02 08:21:12.000000000 +0000 @@ -60,7 +60,6 @@ dbxFilename = "dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f" // Filename in efivarfs for accessing the EFI forbidden signature database mokListFilename = "MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23" // Filename in efivarfs for accessing a runtime copy of the shim MOK database - uefiDriverPCR = 2 // UEFI Drivers and UEFI Applications PCR secureBootPCR = 7 // Secure Boot Policy Measurements PCR returningFromEfiApplicationEvent = "Returning from EFI Application from Boot Option" // EV_EFI_ACTION index 2: "Attempt to execute code from Boot Option was unsuccessful" @@ -515,50 +514,6 @@ return updates, nil } -// secureBootVerificationEvent corresponds to a EV_EFI_VARIABLE_AUTHORITY event and an indicator of whether the event -// was recorded before the transition to OS-present. -type secureBootVerificationEvent struct { - *tcglog.Event - measuredInPreOS bool -} - -// identifyInitialOSLaunchVerificationEvent finds the secure boot verification event associated with the verification of the initial -// OS EFI image. -func identifyInitialOSLaunchVerificationEvent(events []*tcglog.Event) (*secureBootVerificationEvent, error) { - preOS := true - var lastEvent *tcglog.Event - var lastEventIsPreOS bool - - for _, e := range events { - if e.EventType == tcglog.EventTypeSeparator && e.PCRIndex != secureBootPCR { - preOS = false - continue - } - - switch e.PCRIndex { - case bootManagerCodePCR: - if e.EventType != tcglog.EventTypeEFIBootServicesApplication { - continue - } - if preOS { - continue - } - if lastEvent == nil { - return nil, errors.New("boot manager image load event occurred without a preceding verification event") - } - return &secureBootVerificationEvent{lastEvent, lastEventIsPreOS}, nil - case secureBootPCR: - if e.EventType != tcglog.EventTypeEFIVariableAuthority { - continue - } - lastEvent = e - lastEventIsPreOS = preOS - } - } - - return nil, errors.New("boot manager image load event not found") -} - // isSecureBootConfigMeasurementEvent determines if event corresponds to the measurement of a secure boot configuration. func isSecureBootConfigMeasurementEvent(event *tcglog.Event, guid tcglog.EFIGUID, name string) bool { if event.PCRIndex != secureBootPCR { @@ -650,9 +605,8 @@ pcrAlgorithm tpm2.HashAlgorithmId loadSequences []*EFIImageLoadEvent - events []*tcglog.Event - initialOSVerificationEvent *secureBootVerificationEvent - sigDbUpdates []*secureBootDbUpdate + events []*tcglog.Event + sigDbUpdates []*secureBootDbUpdate } // secureBootPolicyGenBranch represents a branch of a PCRProtectionProfile. It contains its own PCRProtectionProfile in to which @@ -807,13 +761,24 @@ // these in to this branch. For events corresponding to the measurement of EFI signature databases, measurements are computed based // on the current contents of each database with the supplied updates applied. // -// Processing of the list of events stops when the verification event associated with the loading of the initial OS EFI executable -// is encountered. -func (b *secureBootPolicyGenBranch) processPreOSEvents(events []*tcglog.Event, initialOSVerificationEvent *secureBootVerificationEvent, sigDbUpdates []*secureBootDbUpdate, sigDbUpdateQuirkMode sigDbUpdateQuirkMode) error { - for len(events) > 0 && events[0] != initialOSVerificationEvent.Event { +// Processing of the list of events stops after transitioning from pre-OS to OS-present. This transition is indicated when an +// EV_SEPARATOR event has been measured to any of PCRs 0-6 AND PCR 7. This handles 2 different firmware behaviours: +// - Some firmware implementations signal the transition by measuring EV_SEPARATOR events to PCRs 0-7 at the same time. +// - Other firmware implementations measure a EV_SEPARATOR event to PCR 7 immediately after measuring the secure boot +// configuration, which is before the transition to OS-present. In this case, processing of pre-OS events in PCR 7 +// must continue until an EV_SEPARATOR event is encountered in PCRs 0-6. On firmware implmentations that support +// secure boot verification of EFI drivers, these verification events will be recorded to PCR 7 after the +// EV_SEPARATOR event in PCR 7 but before the EV_SEPARATOR events in PCRs 0-6. +func (b *secureBootPolicyGenBranch) processPreOSEvents(events []*tcglog.Event, sigDbUpdates []*secureBootDbUpdate, sigDbUpdateQuirkMode sigDbUpdateQuirkMode) error { + osPresent := false + seenSecureBootPCRSeparator := false + + for len(events) > 0 { e := events[0] events = events[1:] switch { + case e.PCRIndex < secureBootPCR && e.EventType == tcglog.EventTypeSeparator: + osPresent = true case isKEKMeasurementEvent(e): if err := b.processKEKMeasurementEvent(sigDbUpdates, sigDbUpdateQuirkMode); err != nil { return xerrors.Errorf("cannot process KEK measurement event: %w", err) @@ -830,21 +795,16 @@ b.extendFirmwareVerificationMeasurement(tpm2.Digest(e.Digests[tcglog.AlgorithmId(b.gen.pcrAlgorithm)])) case e.PCRIndex == secureBootPCR: b.extendMeasurement(tpm2.Digest(e.Digests[tcglog.AlgorithmId(b.gen.pcrAlgorithm)])) + if e.EventType == tcglog.EventTypeSeparator { + seenSecureBootPCRSeparator = true + } } - } - - if len(events) == 0 { - return nil - } - if !initialOSVerificationEvent.measuredInPreOS { - return nil + if osPresent && seenSecureBootPCRSeparator { + break + } } - // The verification event associated with the initial OS load event was recorded before the transition to OS-present, which means - // it was used to authenticate a UEFI driver or sysprep application load, so we need to keep it in the profile. - b.extendFirmwareVerificationMeasurement(tpm2.Digest(initialOSVerificationEvent.Digests[tcglog.AlgorithmId(b.gen.pcrAlgorithm)])) - return nil } @@ -1148,7 +1108,7 @@ var roots []*secureBootPolicyGenBranch for i := 0; i <= len(g.sigDbUpdates); i++ { branch := &secureBootPolicyGenBranch{gen: g, profile: NewPCRProtectionProfile(), dbUpdateLevel: i} - if err := branch.processPreOSEvents(g.events, g.initialOSVerificationEvent, g.sigDbUpdates[0:i], sigDbUpdateQuirkMode); err != nil { + if err := branch.processPreOSEvents(g.events, g.sigDbUpdates[0:i], sigDbUpdateQuirkMode); err != nil { return xerrors.Errorf("cannot process pre-OS events from event log: %w", err) } roots = append(roots, branch) @@ -1379,13 +1339,7 @@ return xerrors.Errorf("cannot build list of UEFI signature DB updates: %w", err) } - // Find the verification event corresponding to the load of the first OS binary. - initialOSVerificationEvent, err := identifyInitialOSLaunchVerificationEvent(log.Events) - if err != nil { - return xerrors.Errorf("cannot identify initial OS launch verification event: %w", err) - } - - gen := &secureBootPolicyGen{params.PCRAlgorithm, params.LoadSequences, log.Events, initialOSVerificationEvent, sigDbUpdates} + gen := &secureBootPolicyGen{params.PCRAlgorithm, params.LoadSequences, log.Events, sigDbUpdates} profile1 := NewPCRProtectionProfile() if err := gen.run(profile1, sigDbUpdateQuirkModeNone); err != nil { diff -Nru snapd-2.48+20.04/vendor/vendor.json snapd-2.48.3+20.04/vendor/vendor.json --- snapd-2.48+20.04/vendor/vendor.json 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/vendor/vendor.json 2021-02-02 08:21:12.000000000 +0000 @@ -116,10 +116,10 @@ "revisionTime": "2017-09-28T14:21:59Z" }, { - "checksumSHA1": "G2sH9o/0sihaKYbjmfWBOFU3Avs=", + "checksumSHA1": "QOWBbcZJqGF5v2rtNSjsGGLrUUE=", "path": "github.com/snapcore/secboot", - "revision": "fa14f1ac3b14d38025312da287728054f7c06b67", - "revisionTime": "2020-11-11T08:01:43Z" + "revision": "027522efe9c7dc36adde13ea8ab030d3dfa34267", + "revisionTime": "2020-11-19T14:48:04Z" }, { "checksumSHA1": "c7jHLQSWFWbymTcFWZMQH0C5Wik=", diff -Nru snapd-2.48+20.04/wrappers/services_gen_test.go snapd-2.48.3+20.04/wrappers/services_gen_test.go --- snapd-2.48+20.04/wrappers/services_gen_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/wrappers/services_gen_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -366,7 +366,7 @@ Command: "bin/foo start", Daemon: "simple", DaemonScope: snap.SystemDaemon, - Plugs: map[string]*snap.PlugInfo{"network-bind": {}}, + Plugs: map[string]*snap.PlugInfo{"network-bind": {Interface: "network-bind"}}, Sockets: map[string]*snap.SocketInfo{ "sock1": { Name: "sock1", diff -Nru snapd-2.48+20.04/wrappers/services.go snapd-2.48.3+20.04/wrappers/services.go --- snapd-2.48+20.04/wrappers/services.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/wrappers/services.go 2021-02-02 08:21:12.000000000 +0000 @@ -31,6 +31,7 @@ "text/template" "time" + "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/osutil/sys" @@ -65,7 +66,7 @@ return nil, err } - return genServiceFile(app, opts), nil + return genServiceFile(app, opts) } func stopUserServices(cli *client.Client, inter interacter, services ...string) error { @@ -678,11 +679,43 @@ return names } -func genServiceFile(appInfo *snap.AppInfo, opts *AddSnapServicesOptions) []byte { +func genServiceFile(appInfo *snap.AppInfo, opts *AddSnapServicesOptions) ([]byte, error) { if opts == nil { opts = &AddSnapServicesOptions{} } + // assemble all of the service directive snippets for all interfaces that + // this service needs to include in the generated systemd file + + // use an ordered set to ensure we don't duplicate any keys from interfaces + // that specify the same snippet + + // TODO: maybe we should error if multiple interfaces specify different + // values for the same directive, otherwise one of them will overwrite the + // other? What happens right now is that the snippet from the plug that + // comes last will win in the case of directives that can have only one + // value, but for some directives, systemd combines their values into a + // list. + ifaceServiceSnippets := &strutil.OrderedSet{} + + for _, plug := range appInfo.Plugs { + iface, err := interfaces.ByName(plug.Interface) + if err != nil { + return nil, fmt.Errorf("error processing plugs while generating service unit for %v: %v", appInfo.SecurityTag(), err) + } + snips, err := interfaces.PermanentPlugServiceSnippets(iface, plug) + if err != nil { + return nil, fmt.Errorf("error processing plugs while generating service unit for %v: %v", appInfo.SecurityTag(), err) + } + for _, snip := range snips { + ifaceServiceSnippets.Put(snip) + } + } + + // join the service snippets into one string to be included in the + // template + ifaceSpecifiedServiceSnippet := strings.Join(ifaceServiceSnippets.Items(), "\n") + serviceTemplate := `[Unit] # Auto-generated, DO NOT EDIT Description=Service for snap application {{.App.Snap.InstanceName}}.{{.App.Name}} @@ -743,6 +776,9 @@ {{- if .OOMAdjustScore }} OOMScoreAdjust={{.OOMAdjustScore}} {{- end}} +{{- if .InterfaceServiceSnippets}} +{{.InterfaceServiceSnippets}} +{{- end}} {{- if not .App.Sockets}} [Install] @@ -787,25 +823,28 @@ wrapperData := struct { App *snap.AppInfo - Restart string - WorkingDir string - StopTimeout time.Duration - StartTimeout time.Duration - ServicesTarget string - PrerequisiteTarget string - MountUnit string - Remain string - KillMode string - KillSignal string - OOMAdjustScore int - Before []string - After []string + Restart string + WorkingDir string + StopTimeout time.Duration + StartTimeout time.Duration + ServicesTarget string + PrerequisiteTarget string + MountUnit string + Remain string + KillMode string + KillSignal string + OOMAdjustScore int + Before []string + After []string + InterfaceServiceSnippets string Home string EnvVars string }{ App: appInfo, + InterfaceServiceSnippets: ifaceSpecifiedServiceSnippet, + Restart: restartCond, StopTimeout: serviceStopTimeout(appInfo), StartTimeout: time.Duration(appInfo.StartTimeout), @@ -849,7 +888,7 @@ logger.Panicf("Unable to execute template: %v", err) } - return templateOut.Bytes() + return templateOut.Bytes(), nil } func genServiceSocketFile(appInfo *snap.AppInfo, socketName string) []byte { diff -Nru snapd-2.48+20.04/wrappers/services_test.go snapd-2.48.3+20.04/wrappers/services_test.go --- snapd-2.48+20.04/wrappers/services_test.go 2020-11-19 16:51:02.000000000 +0000 +++ snapd-2.48.3+20.04/wrappers/services_test.go 2021-02-02 08:21:12.000000000 +0000 @@ -32,6 +32,9 @@ . "gopkg.in/check.v1" "github.com/snapcore/snapd/dirs" + // imported to ensure actual interfaces are defined, + // in production this is guaranteed by ifacestate + _ "github.com/snapcore/snapd/interfaces/builtin" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/progress" @@ -103,12 +106,32 @@ content, err := ioutil.ReadFile(svcFile) c.Assert(err, IsNil) - verbs := []string{"Start", "Stop", "StopPost"} - cmds := []string{"", " --command=stop", " --command=post-stop"} - for i := range verbs { - expected := fmt.Sprintf("Exec%s=/usr/bin/snap run%s hello-snap.svc1", verbs[i], cmds[i]) - c.Check(string(content), Matches, "(?ms).*^"+regexp.QuoteMeta(expected)) // check.v1 adds ^ and $ around the regexp provided - } + dir := filepath.Join(dirs.GlobalRootDir, "snap", "hello-snap", "12.mount") + c.Assert(string(content), Equals, fmt.Sprintf(`[Unit] +# Auto-generated, DO NOT EDIT +Description=Service for snap application hello-snap.svc1 +Requires=%[1]s +Wants=network.target +After=%[1]s network.target snapd.apparmor.service +X-Snappy=yes + +[Service] +EnvironmentFile=-/etc/environment +ExecStart=/usr/bin/snap run hello-snap.svc1 +SyslogIdentifier=hello-snap.svc1 +Restart=on-failure +WorkingDirectory=%[2]s/var/snap/hello-snap/12 +ExecStop=/usr/bin/snap run --command=stop hello-snap.svc1 +ExecStopPost=/usr/bin/snap run --command=post-stop hello-snap.svc1 +TimeoutStopSec=30 +Type=forking + +[Install] +WantedBy=multi-user.target +`, + systemd.EscapeUnitNamePath(dir), + dirs.GlobalRootDir, + )) s.sysdLog = nil err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings) @@ -128,6 +151,155 @@ c.Check(s.sysdLog[1], DeepEquals, []string{"daemon-reload"}) } +func (s *servicesTestSuite) TestAddSnapServicesWithInterfaceSnippets(c *C) { + tt := []struct { + comment string + plugSnippet string + }{ + // just single bare interfaces with no attributes + { + "docker-support", + ` + plugs: + - docker-support`, + }, + { + "k8s-support", + ` + plugs: + - kubernetes-support`, + }, + { + "lxd-support", + ` + plugs: + - lxd-support +`, + }, + { + "greengrass-support", + ` + plugs: + - greengrass-support +`, + }, + + // multiple interfaces that require Delegate=true, but only one is + // generated + + { + "multiple interfaces that require Delegate=true", + ` + plugs: + - docker-support + - kubernetes-support`, + }, + + // interfaces with flavor attributes + + { + "k8s-support with kubelet", + ` + plugs: + - kubelet +plugs: + kubelet: + interface: kubernetes-support + flavor: kubelet +`, + }, + { + "k8s-support with kubeproxy", + ` + plugs: + - kubeproxy +plugs: + kubeproxy: + interface: kubernetes-support + flavor: kubeproxy +`, + }, + { + "greengrass-support with legacy-container flavor", + ` + plugs: + - greengrass +plugs: + greengrass: + interface: greengrass-support + flavor: legacy-container +`, + }, + } + + for _, t := range tt { + comment := Commentf(t.comment) + info := snaptest.MockSnap(c, packageHello+` + svc1: + daemon: simple +`+t.plugSnippet, + &snap.SideInfo{Revision: snap.R(12)}) + svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + + err := wrappers.AddSnapServices(info, nil, nil, progress.Null) + c.Assert(err, IsNil, comment) + c.Check(s.sysdLog, DeepEquals, [][]string{ + {"enable", "snap.hello-snap.svc1.service"}, + {"daemon-reload"}, + }, comment) + + content, err := ioutil.ReadFile(svcFile) + c.Assert(err, IsNil, comment) + + dir := filepath.Join(dirs.GlobalRootDir, "snap", "hello-snap", "12.mount") + c.Assert(string(content), Equals, fmt.Sprintf(`[Unit] +# Auto-generated, DO NOT EDIT +Description=Service for snap application hello-snap.svc1 +Requires=%[1]s +Wants=network.target +After=%[1]s network.target snapd.apparmor.service +X-Snappy=yes + +[Service] +EnvironmentFile=-/etc/environment +ExecStart=/usr/bin/snap run hello-snap.svc1 +SyslogIdentifier=hello-snap.svc1 +Restart=on-failure +WorkingDirectory=%[2]s/var/snap/hello-snap/12 +TimeoutStopSec=30 +Type=simple +Delegate=true + +[Install] +WantedBy=multi-user.target +`, + systemd.EscapeUnitNamePath(dir), + dirs.GlobalRootDir, + ), comment) + + s.sysdLog = nil + err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings) + c.Assert(err, IsNil, comment) + c.Assert(s.sysdLog, HasLen, 2, comment) + c.Check(s.sysdLog, DeepEquals, [][]string{ + {"stop", filepath.Base(svcFile)}, + {"show", "--property=ActiveState", "snap.hello-snap.svc1.service"}, + }, comment) + + s.sysdLog = nil + err = wrappers.RemoveSnapServices(info, progress.Null) + c.Assert(err, IsNil, comment) + c.Check(osutil.FileExists(svcFile), Equals, false, comment) + c.Assert(s.sysdLog, HasLen, 2, comment) + c.Check(s.sysdLog, DeepEquals, [][]string{ + {"disable", filepath.Base(svcFile)}, + {"daemon-reload"}, + }, comment) + + s.sysdLog = nil + } +} + func (s *servicesTestSuite) TestAddSnapServicesAndRemoveUserDaemons(c *C) { info := snaptest.MockSnap(c, packageHello+` svc1: