From 395e4886135cda05135c014f81d99660162c6abe Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 27 Oct 2023 12:13:31 +0100 Subject: [PATCH 1/5] efi: Make use of the grub's prefix for detection Avoid relying on the signature in order to detect an Ubuntu production grub binary for binaries without a SBAT section, as this breaks test cases in snapd where grub is re-signed. Instead, obtain the prefix from the "mods" section (which is set to "/EFI/ubuntu" in Ubuntu). --- efi/efi_test.go | 51 ++++++++++- efi/export_test.go | 10 +++ efi/grub.go | 158 +++++++++++++++++++++++++++++++++ efi/image_load_handler_test.go | 13 ++- efi/image_rules.go | 12 +++ efi/image_rules_defs.go | 14 ++- efi/image_rules_defs_test.go | 27 +++++- efi/pcr_profile_test.go | 3 + efi/pe.go | 6 ++ 9 files changed, 285 insertions(+), 9 deletions(-) create mode 100644 efi/grub.go diff --git a/efi/efi_test.go b/efi/efi_test.go index 5877d12b..0663ed71 100644 --- a/efi/efi_test.go +++ b/efi/efi_test.go @@ -137,6 +137,8 @@ type mockPeImageHandle struct { func (*mockPeImageHandle) Close() error { return nil } func (h *mockPeImageHandle) Source() Image { return h.mockImage } +func (h *mockPeImageHandle) Machine() uint16 { return 0 } + func (h *mockPeImageHandle) OpenSection(name string) *io.SectionReader { data, exists := h.sections[name] if !exists { @@ -176,6 +178,10 @@ func (h *mockPeImageHandle) newShimImageHandle() *mockShimImageHandle { return &mockShimImageHandle{mockPeImageHandle: h} } +func (h *mockPeImageHandle) newGrubImageHandle() *mockGrubImageHandle { + return &mockGrubImageHandle{mockPeImageHandle: h} +} + type mockShimImageHandle struct { *mockPeImageHandle } @@ -205,6 +211,14 @@ func (h *mockShimImageHandle) ReadSbatLevel() (ShimSbatLevel, error) { return *h.shimSbatLevel, nil } +type mockGrubImageHandle struct { + *mockPeImageHandle +} + +func (h *mockGrubImageHandle) Prefix() (string, error) { + return h.grubPrefix, nil +} + type mockImage struct { sections map[string][]byte sbat []SbatComponent @@ -216,6 +230,8 @@ type mockImage struct { shimVendorDb efi.SignatureDatabase shimVendorDbFormat ShimVendorCertFormat shimSbatLevel *ShimSbatLevel + + grubPrefix string } func newMockImage() *mockImage { @@ -278,6 +294,11 @@ func (i *mockImage) withShimSbatLevel(sbatLevel ShimSbatLevel) *mockImage { return i } +func (i *mockImage) withGrubPrefix(prefix string) *mockImage { + i.grubPrefix = prefix + return i +} + func newMockUbuntuShimImage15a(c *C) *mockImage { return newMockImage(). appendSignatures(efitest.ReadWinCertificateAuthenticodeDetached(c, shimUbuntuSig1)). @@ -318,7 +339,8 @@ func newMockUbuntuShimImage15_7(c *C) *mockImage { func newMockUbuntuGrubImage1(c *C) *mockImage { return newMockImage(). appendSignatures(efitest.ReadWinCertificateAuthenticodeDetached(c, grubUbuntuSig1)). - addSection("mods", nil) + addSection("mods", nil). + withGrubPrefix("/EFI/ubuntu") } func newMockUbuntuGrubImage2(c *C) *mockImage { @@ -328,7 +350,8 @@ func newMockUbuntuGrubImage2(c *C) *mockImage { withSbat([]SbatComponent{ {Name: "grub"}, {Name: "grub.ubuntu"}, - }) + }). + withGrubPrefix("/EFI/ubuntu") } func newMockUbuntuGrubImage3(c *C) *mockImage { @@ -338,7 +361,8 @@ func newMockUbuntuGrubImage3(c *C) *mockImage { withSbat([]SbatComponent{ {Name: "grub"}, {Name: "grub.ubuntu"}, - }) + }). + withGrubPrefix("/EFI/ubuntu") } func newMockUbuntuKernelImage1(c *C) *mockImage { @@ -433,6 +457,27 @@ func (m *mockShimImageHandleMixin) TearDownTest(c *C) { } } +type mockGrubImageHandleMixin struct { + restore func() +} + +func (m *mockGrubImageHandleMixin) SetUpTest(c *C) { + orig := NewGrubImageHandle + m.restore = MockNewGrubImageHandle(func(image PeImageHandle) GrubImageHandle { + h, ok := image.(*mockPeImageHandle) + if !ok { + return orig(image) + } + return h.newGrubImageHandle() + }) +} + +func (m *mockGrubImageHandleMixin) TearDownTest(c *C) { + if m.restore != nil { + m.restore() + } +} + type mockImageLoadHandlerMap map[Image]ImageLoadHandler func (h mockImageLoadHandlerMap) LookupHandler(image PeImageHandle) (ImageLoadHandler, error) { diff --git a/efi/export_test.go b/efi/export_test.go index 107a65b5..425a1221 100644 --- a/efi/export_test.go +++ b/efi/export_test.go @@ -58,6 +58,7 @@ var ( MustParseShimVersion = mustParseShimVersion NewestSbatLevel = newestSbatLevel NewFwLoadHandler = newFwLoadHandler + NewGrubImageHandle = newGrubImageHandle NewImageLoadHandlerLazyMap = newImageLoadHandlerLazyMap NewImageRule = newImageRule NewImageRules = newImageRules @@ -86,6 +87,7 @@ var ( // unexported members of some unexported types. type FwContext = fwContext type GrubFlags = grubFlags +type GrubImageHandle = grubImageHandle type GrubLoadHandler = grubLoadHandler type ImageLoadHandler = imageLoadHandler type ImageLoadHandlerMap = imageLoadHandlerMap @@ -163,6 +165,14 @@ func MockNewFwLoadHandler(fn func(*tcglog.Log) ImageLoadHandler) (restore func() } } +func MockNewGrubImageHandle(fn func(peImageHandle) grubImageHandle) (restore func()) { + orig := newGrubImageHandle + newGrubImageHandle = fn + return func() { + newGrubImageHandle = orig + } +} + func MockNewShimImageHandle(fn func(peImageHandle) shimImageHandle) (restore func()) { orig := newShimImageHandle newShimImageHandle = fn diff --git a/efi/grub.go b/efi/grub.go new file mode 100644 index 00000000..6e3d5a3a --- /dev/null +++ b/efi/grub.go @@ -0,0 +1,158 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2023 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 efi + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "math" + + pe "github.com/snapcore/secboot/internal/pe1.14" +) + +const grubObjTypePrefix uint32 = 3 + +type grubModuleHeader struct { + Type uint32 + Size uint32 +} + +const grubModuleMagic uint32 = 0x676d696d + +type grubModuleInfo32 struct { + Magic uint32 + Offset uint32 + Size uint32 +} + +type grubModuleInfo64 struct { + Magic uint32 + Padding uint32 + Offset uint64 + Size uint64 +} + +type grubModule struct { + Type uint32 + *io.SectionReader +} + +type grubImageHandle interface { + peImageHandle + + Prefix() (string, error) +} + +type grubImageHandleImpl struct { + peImageHandle +} + +// newGrubImageHandle returns a new grubImageHandle for the supplied peImageHandle. +var newGrubImageHandle = func(image peImageHandle) grubImageHandle { + return &grubImageHandleImpl{peImageHandle: image} +} + +func (h *grubImageHandleImpl) mods() ([]grubModule, error) { + section := h.OpenSection("mods") + if section == nil { + return nil, errors.New("no mods section") + } + + var r *io.SectionReader + switch h.Machine() { + case pe.IMAGE_FILE_MACHINE_AMD64, pe.IMAGE_FILE_MACHINE_ARM64, pe.IMAGE_FILE_MACHINE_RISCV64: + var info grubModuleInfo64 + if err := binary.Read(section, binary.LittleEndian, &info); err != nil { + return nil, fmt.Errorf("cannot obtain modules info: %w", err) + } + if info.Magic != grubModuleMagic { + return nil, errors.New("invalid modules magic") + } + if info.Size > math.MaxInt64 { + return nil, errors.New("invalid modules offset") + } + if info.Size > math.MaxInt64 || info.Size < uint64(binary.Size(info)) { + return nil, errors.New("invalid modules size") + } + r = io.NewSectionReader(section, int64(info.Offset), int64(info.Size)-int64(binary.Size(info))) + case pe.IMAGE_FILE_MACHINE_ARM, pe.IMAGE_FILE_MACHINE_I386, pe.IMAGE_FILE_MACHINE_RISCV32: + var info grubModuleInfo32 + if err := binary.Read(section, binary.LittleEndian, &info); err != nil { + return nil, fmt.Errorf("cannot obtain modules info: %w", err) + } + if info.Magic != grubModuleMagic { + return nil, errors.New("invalid module magic") + } + if info.Size < uint32(binary.Size(info.Size)) { + return nil, errors.New("invalid modules size") + } + r = io.NewSectionReader(section, int64(info.Offset), int64(info.Size)-int64(binary.Size(info))) + default: + return nil, fmt.Errorf("unrecognized machine: %d", h.Machine()) + } + + var mods []grubModule + + for { + var hdr grubModuleHeader + if err := binary.Read(r, binary.LittleEndian, &hdr); err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("cannot obtain module header: %w", err) + } + + offset, _ := r.Seek(0, io.SeekCurrent) + size := int64(hdr.Size) - int64(binary.Size(hdr)) + mods = append(mods, grubModule{ + Type: hdr.Type, + SectionReader: io.NewSectionReader(r, offset, size), + }) + + if _, err := r.Seek(size, io.SeekCurrent); err != nil { + return nil, fmt.Errorf("cannot seek to next module: %w", err) + } + } + + return mods, nil +} + +func (h *grubImageHandleImpl) Prefix() (string, error) { + mods, err := h.mods() + if err != nil { + return "", err + } + + for _, mod := range mods { + if mod.Type != grubObjTypePrefix { + continue + } + + prefix, err := io.ReadAll(newCstringReader(mod)) + if err != nil { + return "", fmt.Errorf("cannot obtain prefix: %w", err) + } + return string(prefix), nil + } + + return "", nil +} diff --git a/efi/image_load_handler_test.go b/efi/image_load_handler_test.go index 88436b3f..33a8f82d 100644 --- a/efi/image_load_handler_test.go +++ b/efi/image_load_handler_test.go @@ -48,6 +48,17 @@ func (c *mockImageLoadHandlerConstructor) NewImageLoadHandler(image PeImageHandl type imageLoadHandlerSuite struct { mockShimImageHandleMixin + mockGrubImageHandleMixin +} + +func (s *imageLoadHandlerSuite) SetUpTest(c *C) { + s.mockShimImageHandleMixin.SetUpTest(c) + s.mockGrubImageHandleMixin.SetUpTest(c) +} + +func (s *imageLoadHandlerSuite) TearDownTest(c *C) { + s.mockShimImageHandleMixin.TearDownTest(c) + s.mockGrubImageHandleMixin.TearDownTest(c) } var _ = Suite(&imageLoadHandlerSuite{}) @@ -131,5 +142,5 @@ func (s *imageLoadHandlerSuite) TestDefaultLookupFallback(c *C) { handler, err := m.LookupHandler(image.newPeImageHandle()) c.Assert(err, IsNil) c.Assert(handler, testutil.ConvertibleTo, &GrubLoadHandler{}) - c.Check(handler, DeepEquals, new(GrubLoadHandler)) + c.Check(handler.(*GrubLoadHandler).Flags, Equals, GrubChainloaderUsesShimProtocol) } diff --git a/efi/image_rules.go b/efi/image_rules.go index 343f5b15..637832e8 100644 --- a/efi/image_rules.go +++ b/efi/image_rules.go @@ -241,3 +241,15 @@ func (p *shimVersionPredicate) Matches(image peImageHandle) (bool, error) { return false, fmt.Errorf("invalid shim version operator %s", p.operator) } } + +type grubHasPrefix string + +func (p grubHasPrefix) Matches(image peImageHandle) (bool, error) { + grub := newGrubImageHandle(image) + prefix, err := grub.Prefix() + if err != nil { + return false, fmt.Errorf("cannot obtain grub prefix: %w", err) + } + + return prefix == string(p), nil +} diff --git a/efi/image_rules_defs.go b/efi/image_rules_defs.go index 5b3e12fb..b30ac284 100644 --- a/efi/image_rules_defs.go +++ b/efi/image_rules_defs.go @@ -163,7 +163,7 @@ func makeMicrosoftUEFICASecureBootNamespaceRules() *secureBootNamespaceRules { ), imageMatchesAll( imageSectionExists("mods"), - imageSignedByOrganization("Canonical Ltd."), + grubHasPrefix("/EFI/ubuntu"), ), ), newGrubLoadHandlerConstructor(grubChainloaderUsesShimProtocol).New, @@ -209,14 +209,22 @@ func makeFallbackImageRules() *imageRules { imageSectionExists(".vendor_cert"), newShimLoadHandler, ), + // Ubuntu grub + newImageRule( + "grub", + imageMatchesAll( + imageSectionExists("mods"), + grubHasPrefix("/EFI/ubuntu"), + ), + newGrubLoadHandlerConstructor(grubChainloaderUsesShimProtocol).New, + ), // Grub newImageRule( "grub", imageSectionExists("mods"), newGrubLoadHandler, ), - // TODO: add rules for Ubuntu Core UKI and Ubuntu grub that are not part of - // the MS UEFI CA? + // TODO: add rules for Ubuntu Core UKIs that are not part of the MS UEFI CA // // Catch-all for unrecognized leaf images newImageRule( diff --git a/efi/image_rules_defs_test.go b/efi/image_rules_defs_test.go index 8f37b1b9..6ab36195 100644 --- a/efi/image_rules_defs_test.go +++ b/efi/image_rules_defs_test.go @@ -34,6 +34,17 @@ import ( type imageRulesDefsSuite struct { mockShimImageHandleMixin + mockGrubImageHandleMixin +} + +func (s *imageRulesDefsSuite) SetUpTest(c *C) { + s.mockShimImageHandleMixin.SetUpTest(c) + s.mockGrubImageHandleMixin.SetUpTest(c) +} + +func (s *imageRulesDefsSuite) TearDownTest(c *C) { + s.mockShimImageHandleMixin.TearDownTest(c) + s.mockGrubImageHandleMixin.TearDownTest(c) } var _ = Suite(&imageRulesDefsSuite{}) @@ -237,7 +248,7 @@ func (s *imageRulesDefsSuite) TestFallbackNewImageLoadHandlerShim(c *C) { c.Check(shimHandler.SbatLevel, DeepEquals, ShimSbatLevel{[]byte("sbat,1,2022111500\nshim,2\ngrub,3\n"), []byte("sbat,1,2022052400\ngrub,2\n")}) } -func (s *imageRulesDefsSuite) TestFallbackNewImageLoadHandlerGrub(c *C) { +func (s *imageRulesDefsSuite) TestFallbackNewImageLoadHandlerUbuntuGrub(c *C) { // verify that grub is recognized by the fallback rules image := newMockUbuntuGrubImage1(c) @@ -245,9 +256,21 @@ func (s *imageRulesDefsSuite) TestFallbackNewImageLoadHandlerGrub(c *C) { handler, err := rules.NewImageLoadHandler(image.newPeImageHandle()) c.Assert(err, IsNil) c.Assert(handler, testutil.ConvertibleTo, &GrubLoadHandler{}) - c.Check(handler.(*GrubLoadHandler), DeepEquals, new(GrubLoadHandler)) + c.Check(handler.(*GrubLoadHandler).Flags, Equals, GrubChainloaderUsesShimProtocol) } +func (s *imageRulesDefsSuite) TestFallbackNewImageLoadHandlerGrub(c *C) { + // verify that grub is recognized by the fallback rules + image := newMockImage(). + addSection("mods", nil). + withGrubPrefix("/EFI/debian") + + rules := MakeFallbackImageRules() + handler, err := rules.NewImageLoadHandler(image.newPeImageHandle()) + c.Assert(err, IsNil) + c.Assert(handler, testutil.ConvertibleTo, &GrubLoadHandler{}) + c.Check(handler.(*GrubLoadHandler), DeepEquals, new(GrubLoadHandler)) +} func (s *imageRulesDefsSuite) TestFallbackNewImageLoadHandlerNull(c *C) { // verify that an unrecognized leaf image is recognized by the fallback rules image := newMockImage() diff --git a/efi/pcr_profile_test.go b/efi/pcr_profile_test.go index 73d8cdb5..d494911c 100644 --- a/efi/pcr_profile_test.go +++ b/efi/pcr_profile_test.go @@ -586,16 +586,19 @@ type pcrProfileSuite struct { restoreNewShimImageHandle func() mockImageHandleMixin mockShimImageHandleMixin + mockGrubImageHandleMixin } func (s *pcrProfileSuite) SetUpTest(c *C) { s.mockImageHandleMixin.SetUpTest(c) s.mockShimImageHandleMixin.SetUpTest(c) + s.mockGrubImageHandleMixin.SetUpTest(c) } func (s *pcrProfileSuite) TearDownTest(c *C) { s.mockImageHandleMixin.TearDownTest(c) s.mockShimImageHandleMixin.TearDownTest(c) + s.mockGrubImageHandleMixin.TearDownTest(c) } var _ = Suite(&pcrProfileSuite{}) diff --git a/efi/pe.go b/efi/pe.go index aba91d88..057947dc 100644 --- a/efi/pe.go +++ b/efi/pe.go @@ -51,6 +51,8 @@ type peImageHandle interface { // Source returns the image source Source() Image + Machine() uint16 + // OpenSection returns a new io.SectionReader for the section with // the specified name, or nil if no section exists. OpenSection(name string) *io.SectionReader @@ -109,6 +111,10 @@ func (h *peImageHandleImpl) Source() Image { return h.source } +func (h *peImageHandleImpl) Machine() uint16 { + return h.pefile.Machine +} + func (h *peImageHandleImpl) OpenSection(name string) *io.SectionReader { section := h.pefile.Section(name) if section == nil { From a0a435a2a2c0c8ea0fc320bcabec2742bf394b96 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Tue, 28 Nov 2023 10:14:15 +0000 Subject: [PATCH 2/5] efi: add tests for grubHasPrefix --- efi/efi_test.go | 3 +++ efi/export_test.go | 1 + efi/image_rules_defs_test.go | 1 + efi/image_rules_test.go | 46 ++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/efi/efi_test.go b/efi/efi_test.go index 0663ed71..ad47bda1 100644 --- a/efi/efi_test.go +++ b/efi/efi_test.go @@ -216,6 +216,9 @@ type mockGrubImageHandle struct { } func (h *mockGrubImageHandle) Prefix() (string, error) { + if h.grubPrefix == "" { + return "", errors.New("no prefix") + } return h.grubPrefix, nil } diff --git a/efi/export_test.go b/efi/export_test.go index 425a1221..e75402e4 100644 --- a/efi/export_test.go +++ b/efi/export_test.go @@ -87,6 +87,7 @@ var ( // unexported members of some unexported types. type FwContext = fwContext type GrubFlags = grubFlags +type GrubHasPrefix = grubHasPrefix type GrubImageHandle = grubImageHandle type GrubLoadHandler = grubLoadHandler type ImageLoadHandler = imageLoadHandler diff --git a/efi/image_rules_defs_test.go b/efi/image_rules_defs_test.go index 6ab36195..9f3422aa 100644 --- a/efi/image_rules_defs_test.go +++ b/efi/image_rules_defs_test.go @@ -271,6 +271,7 @@ func (s *imageRulesDefsSuite) TestFallbackNewImageLoadHandlerGrub(c *C) { c.Assert(handler, testutil.ConvertibleTo, &GrubLoadHandler{}) c.Check(handler.(*GrubLoadHandler), DeepEquals, new(GrubLoadHandler)) } + func (s *imageRulesDefsSuite) TestFallbackNewImageLoadHandlerNull(c *C) { // verify that an unrecognized leaf image is recognized by the fallback rules image := newMockImage() diff --git a/efi/image_rules_test.go b/efi/image_rules_test.go index 45fd962d..2f1f689c 100644 --- a/efi/image_rules_test.go +++ b/efi/image_rules_test.go @@ -43,6 +43,17 @@ func (p *mockImagePredicate) Matches(image PeImageHandle) (bool, error) { type imageRulesSuite struct { mockShimImageHandleMixin + mockGrubImageHandleMixin +} + +func (s *imageRulesSuite) SetUpTest(c *C) { + s.mockShimImageHandleMixin.SetUpTest(c) + s.mockGrubImageHandleMixin.SetUpTest(c) +} + +func (s *imageRulesSuite) TearDownTest(c *C) { + s.mockShimImageHandleMixin.TearDownTest(c) + s.mockGrubImageHandleMixin.TearDownTest(c) } var _ = Suite(&imageRulesSuite{}) @@ -492,6 +503,41 @@ func (s *imageRulesSuite) TestShimVersionErr(c *C) { c.Check(err, ErrorMatches, `cannot obtain shim version: no version`) } +func (s *imageRulesSuite) TestGrubHasPrefixTrue1(c *C) { + image := newMockImage().withGrubPrefix("/EFI/ubuntu").newPeImageHandle() + + pred := GrubHasPrefix("/EFI/ubuntu") + match, err := pred.Matches(image) + c.Check(err, IsNil) + c.Check(match, testutil.IsTrue) +} + +func (s *imageRulesSuite) TestGrubHasPrefixTrue2(c *C) { + image := newMockImage().withGrubPrefix("/EFI/debian").newPeImageHandle() + + pred := GrubHasPrefix("/EFI/debian") + match, err := pred.Matches(image) + c.Check(err, IsNil) + c.Check(match, testutil.IsTrue) +} + +func (s *imageRulesSuite) TestGrubHasPrefixFalse(c *C) { + image := newMockImage().withGrubPrefix("/EFI/debian").newPeImageHandle() + + pred := GrubHasPrefix("/EFI/ubuntu") + match, err := pred.Matches(image) + c.Check(err, IsNil) + c.Check(match, testutil.IsFalse) +} + +func (s *imageRulesSuite) TestGrubHasPrefixErr(c *C) { + image := newMockImage().newPeImageHandle() + + pred := GrubHasPrefix("/EFI/ubuntu") + _, err := pred.Matches(image) + c.Check(err, ErrorMatches, `cannot obtain grub prefix: no prefix`) +} + func (s *imageRulesSuite) TestImageRulesMatch1(c *C) { image := newMockImage().newPeImageHandle() From 8cace2bac1c50216fd19229cac443c2b4a3d7796 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Tue, 28 Nov 2023 13:36:26 +0000 Subject: [PATCH 3/5] efi: add tests for grubImageHandle.Prefix --- efi/grub.go | 3 + efi/grub_test.go | 66 ++++++++++++++++++ efi/pe_test.go | 4 +- efi/testdata/amd64/mockgrub.efi | Bin 0 -> 3761 bytes .../amd64/mockgrub1.efi.signed.shim.1 | Bin 5056 -> 5568 bytes efi/testdata/amd64/mockgrub_debian.efi | Bin 0 -> 3761 bytes efi/testdata/amd64/mockgrub_no_prefix.efi | Bin 0 -> 3761 bytes efi/testdata/amd64/mockshim.efi.signed.1.1.1 | Bin 7304 -> 7304 bytes .../amd64/mockshim.efi.signed.1.2.1+1.1.1 | Bin 9096 -> 9096 bytes .../mockshim_initial_sbat.efi.signed.1.1.1 | Bin 6744 -> 6744 bytes .../amd64/mockshim_no_sbat.efi.signed.1.1.1 | Bin 6176 -> 6176 bytes .../mockshim_no_vendor_cert.efi.signed.1.1.1 | Bin 6800 -> 6800 bytes .../amd64/mockshim_vendor_db.efi.signed.1.1.1 | Bin 7304 -> 7304 bytes efi/testdata/src/grub/Makefile | 7 +- efi/testdata/src/grub/mods.S | 17 +++++ tools/make-efi-testdata/apps.go | 24 +++++-- 16 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 efi/grub_test.go create mode 100644 efi/testdata/amd64/mockgrub.efi create mode 100644 efi/testdata/amd64/mockgrub_debian.efi create mode 100644 efi/testdata/amd64/mockgrub_no_prefix.efi create mode 100644 efi/testdata/src/grub/mods.S diff --git a/efi/grub.go b/efi/grub.go index 6e3d5a3a..49c9bfa6 100644 --- a/efi/grub.go +++ b/efi/grub.go @@ -56,9 +56,12 @@ type grubModule struct { *io.SectionReader } +// grubImageHandle corresponds to a grub image. type grubImageHandle interface { peImageHandle + // Prefix returns the path that grub uses to load its configuration + // from the ESP. Prefix() (string, error) } diff --git a/efi/grub_test.go b/efi/grub_test.go new file mode 100644 index 00000000..4b76f741 --- /dev/null +++ b/efi/grub_test.go @@ -0,0 +1,66 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2023 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 efi_test + +import ( + . "gopkg.in/check.v1" + + . "github.com/snapcore/secboot/efi" +) + +type grubSuite struct{} + +var _ = Suite(&grubSuite{}) + +func (s *grubSuite) TestGrubImageHandlePrefix1(c *C) { + image, err := OpenPeImage(NewFileImage("testdata/amd64/mockgrub.efi")) + c.Assert(err, IsNil) + defer image.Close() + + grubImage := NewGrubImageHandle(image) + + prefix, err := grubImage.Prefix() + c.Check(err, IsNil) + c.Check(prefix, Equals, "/EFI/ubuntu") +} + +func (s *grubSuite) TestGrubImageHandlePrefix2(c *C) { + image, err := OpenPeImage(NewFileImage("testdata/amd64/mockgrub_debian.efi")) + c.Assert(err, IsNil) + defer image.Close() + + grubImage := NewGrubImageHandle(image) + + prefix, err := grubImage.Prefix() + c.Check(err, IsNil) + c.Check(prefix, Equals, "/EFI/debian") +} + +func (s *grubSuite) TestGrubImageHandlePrefixNone(c *C) { + image, err := OpenPeImage(NewFileImage("testdata/amd64/mockgrub_no_prefix.efi")) + c.Assert(err, IsNil) + defer image.Close() + + grubImage := NewGrubImageHandle(image) + + prefix, err := grubImage.Prefix() + c.Check(err, IsNil) + c.Check(prefix, Equals, "") +} diff --git a/efi/pe_test.go b/efi/pe_test.go index 8b0304bc..2098309c 100644 --- a/efi/pe_test.go +++ b/efi/pe_test.go @@ -127,7 +127,7 @@ func (s *peSuite) TestPeImageHandleSbatComponents1(c *C) { func (s *peSuite) TestPeImageHandleSbatComponents2(c *C) { s.testPeImageHandleSbatComponents( - c, "testdata/amd64/mockgrub1.efi.signed.shim.1", + c, "testdata/amd64/mockgrub.efi", []SbatComponent{ {Name: "grub", Generation: 1, VendorName: "Free Software Foundation", VendorPackageName: "grub", VendorVersion: "2.06", VendorUrl: "https://www.gnu.org/software/grub/"}, {Name: "grub.acme", Generation: 1, VendorName: "Acme Corporation", VendorPackageName: "grub", VendorVersion: "1", VendorUrl: "https://acme.invalid/grub"}, @@ -158,7 +158,7 @@ func (s *peSuite) TestPeImageHandleImageDigest1(c *C) { } func (s *peSuite) TestPeImageHandleImageDigest2(c *C) { - s.testPeImageHandleImageDigest(c, "testdata/amd64/mockgrub1.efi.signed.shim.1", crypto.SHA256) + s.testPeImageHandleImageDigest(c, "testdata/amd64/mockgrub.efi", crypto.SHA256) } func (s *peSuite) TestPeImageHandleImageDigestSHA1(c *C) { diff --git a/efi/testdata/amd64/mockgrub.efi b/efi/testdata/amd64/mockgrub.efi new file mode 100644 index 0000000000000000000000000000000000000000..3648f23cea80551735a4e004053a6fc949bad5f3 GIT binary patch literal 3761 zcmeHKy>1gh5T4i&5$7873UU#o1l>q;J3Bw$&CQ+HGf!WBf+_%9LLQ9(4sj)F z`s?2>2WYNue67Qm)#J^>r6gNdI9M@2zR4>{SD>qV%*qwhzQ=C}2Y z4;LZbGRHHnzzUud;x zvDw-qm-r3$`FQmrx z(jw}cNa#?df`Yk@YjlnvR?AAs+UB(zhE}qc$y4k_3D?{2@~wNp1bR3e@+8f{znx0} literal 0 HcmV?d00001 diff --git a/efi/testdata/amd64/mockgrub1.efi.signed.shim.1 b/efi/testdata/amd64/mockgrub1.efi.signed.shim.1 index 6f73204babcaf8729a7c7b546696074306e7aa47..2db4a6f3cbde10a9599c6c83913787263cbfa102 100644 GIT binary patch delta 525 zcmX@0en5Ldn+z)h81OMLZ~_@07}%J&nV2*fn1CFPiHr1jbsB&y76t}~9}Em!lN}k= zC$liBGV)E&FAjT2d2mfQuuC+Sjd34e>JHzEyof8W=NWalAB(dK)L_XT>dS-v+;}ylk$q0*VgI{ZhEfIZxqTcIwiU$A~ C5WIr` delta 457 zcmV;)0XF`?E5Ij^h8zR{0000001N;C2><{9@Bjt^3jzWt00IC200xnwE)4*H00004 z008Aq000V;K>;U|1OX)h43jDW)(I|iVqtUu007sM2?8IpPy(<4lTZODq67xA3`GMV zVXrc1U367|5DvtR+dzOi0D}x9Eqy9v0uq6gpm!1>sWTS0XpL?_+Ppptv$jRM%huf?s|!|DN9G z5du;GRcr@A7wOx+#OpQBCa4-b#a5+V6+IT(dOTn*lPd7)rnJ_DXV7z|4XO(`gT#r% zQaeUbp73;+fe!L$S7F_GIsW2bySN{KwLg77+!UoU7)ifk%RL{44 z!moG5dF8`U$K2Mky*!wYvFP?XzpGd-*-&4>xy%pfDhaS5D)Yltc9z!gqU>vT!4q!r z-O?~F7GWyE^QadByxp#zv<1Y}j7*TBauDIZiBt@%^HGx(T_i_|Mg~bh00000YP-8v diff --git a/efi/testdata/amd64/mockgrub_debian.efi b/efi/testdata/amd64/mockgrub_debian.efi new file mode 100644 index 0000000000000000000000000000000000000000..7a125b1c8818a08a9243411923d7102e093a3a59 GIT binary patch literal 3761 zcmeHKKX21O6n{;i&_EL;7}$E17#OU(hM@yf+R_Ll5J;&QSi(6zoD(@;bhb&$)B!QG z^EDVMrb-MN<1Z!$v;QlF&gk@et!k1&G{}dm5a5Fyxi7Ll41Nk3HC84QJS9et1k9c0_ff3!v4RIUy+_4PN9SCFd^q{bYf z)W|fl45Y6HO;nL+Jg7ofO~w^$99c{rUMhvG2Wp{~f9Ta07WV0XV^($am}i zmt%A{wtqI@$NJgM$=a*4ox{FL*wC8D3e9>Vj!nt}$!r!g6|?7WI!v2T@{Nu9!yNkU z3xM$3&8gM^SV!p`YLz>c$|I-mJoO<$0^3ST4wh~52Zj1-~+dEV0w8CXmCBK6@pOo0rq()Qi1Y)a_u4)dr3AV zAK(<9M?!LwmLW35Ei}%@P@V*W>idJ-oWh_OpQZjP`6lFedeqzS3SWFoCCqiako^ zI;_)s1hEdRl&oWp(vh%|9hfY}vnb&@|6{)Uq&0&cjYd3*Gj43;C3%KNVV(oyqNgPy z?%~Fs89Ow#fYhGDP#9P7Coxc=OE14@%l_2394k84-$GtPu0oh;{TZb#r;%kK0y$`- zibUf<6}oaduHfOpWmTY?q{0%=g(aV8E-WO`NK-qXOlaoBtb9r#Pi~P@6U{AC%O*-L KuA8VY68-^A2Aqlj literal 0 HcmV?d00001 diff --git a/efi/testdata/amd64/mockshim.efi.signed.1.1.1 b/efi/testdata/amd64/mockshim.efi.signed.1.1.1 index 18f8aee9d86eac4596c0a1657f1c65b2cb968910..77103be20ffc5c10c83092d52079c0c0cbf49bd4 100644 GIT binary patch delta 289 zcmV++0p9+IIfyxs*aaw|000V;K>;VT1OdMp1T!--HIv60!GDQ$+7j!NA()J58aq2$ zJ|BT6(ZK+ef2-ryIN3ZCAKPz3$vP6=R)O@LEXFC16n zyt3tqa`N|k5PuDw$A24T!1z4EaN4fSSrzsa6E&~oebtXu zsqOZj%~5ROHbJ%5#wRw;VT1OdMp1Tr-=F_Xs{!GAY+`=5JaW~F*Gdqn;Q zu%aQ-$-alEPEm7nX9Z@3%_KcWEX_mG$UR8k0gqAI+LmMP=ES^N&vFOhy78{^b-*?h z9`Bf?0_-A9zv&ub+~FqjX?+tv=Z>n()(=;OY{V!iWMmAXaTI+QfpY(Zg|T^+nke}K zX4f5yU#Vpt#(y^R;^8!wgqR8^5BI6&6BF!R$D%MpASoB9_t#|_RNEN^^nbAYD^CQ> zL&bWqrRMBysg7bC-zT5jX-%O?`re$fayn|QKjv@D;VT1OdMp1T!--HIv60!GBBFa=P{|ro-bR%3($H z7Ep&c@v0G1d`va!)OX;k9|q?3zbiR>`59X38!EUvlXGl=j0_kb1zGtC7K=uq9=>w*7(C)?k)R&C&>kapLx zL@;QKu$q~dYJZnTG{cj4%}r@bHq@(L#M%kj!DTll%*;T&11y+)>cw2HT$m!xK~WrQ zUDSSOB_D&Kvf_~Kn+8l9g_2_4LS;i~e)H)zJko2tGBcaR$9WK8U)Hb2H1GZtSlVD? z`E<)hy7c&fj6O=_i?OG!i*G1y5g61WOaF+2y@&`FMR?yILOdisIb&6H?O~VfkO(0BSFXA z0MnT)(*z>~){VwyS4=lyR~bb7!+S`wC@D6acgf(pXF$%$Ddgh^Cf?u|l?tBTj8Fvq z=1vJKCuUFN zELj!y6caVCgNLgP4%uimn>h(3Et6bg4KzbsnG!~<)w}Riq2QwIgsPQ55Vw_C`;!7#{_R>2 AMgRZ+ delta 562 zcmV-20?qx1M~Fv|*ahs{000V;K>;VT1OdMp1Tr-=F_Xs{!G9KlrNdl0w=Z`|{eIK` zpFQZq1>r|9y8{7v)LAwRcuYrak8RtGe@z4i*atv1ZrT!nNzZ5X=*xtFm&_f~uRY$a z3d%NLD1&Een?CMOEl~V(2Z)5M(@CiAab(*1A;lHVFffIV%Bz9K92fVq-T;09e0d|}9x%v(^Jz>ph(URY{9 zJ56vn2MoF`xd;fqfc7N#^{=15D#h!H&^mx+MH}GsbQQ8-tywX4pln<%+#-iY8uJzV zF3Yf%`q8(CO2TQ-00qggoXJ~r;X}N$a-MRpAAwVrHd!GAMl8)k(#SnX-vN(N+S-<5?&ie2 zS1wqC}d;|p>Y&_ z7lCsBgoUwrm6|B|0%q48i(jc_9>#w*^Wxz&mV}rJClB|j<`Wa_UB{v@Lm(*^sQ1@p z8dTdE1@wQg{3}lc%tOU`u%+hgZK;l89N#CO+i6XqN&4QLvT{0Vtv}{(%;fiHY+9** zQ>awUW)OzO!B6))QY-|I#HJT8qOr|<9u@jR`|qx}|BW+%HUoq2+r?FsIZ^^El9(^b diff --git a/efi/testdata/amd64/mockshim_initial_sbat.efi.signed.1.1.1 b/efi/testdata/amd64/mockshim_initial_sbat.efi.signed.1.1.1 index 218f6f83e0c7052f0168746ea34a9328c4564fc6..22e4e1424909fef7a4a194f812f06e2278a6ebda 100644 GIT binary patch delta 289 zcmV++0p9-DG}ttd*ab6|000V;K>;VT1Obm11T!--HItVZ!GCwsIHBfV$9xgb;leyl zGAzrlxwT)!^n7SZUeoGz$DE=3Sw)qgA#t7exx#wX17bMsjnNTG+QSA80R5AMSa5r5 zHypw05De>OR)2PLXSSy;2M4l)Wj*vn(G}i8tCedNmt; zIq;VT1Obm11Tr-=F_V`V!GEtqwb)x?{=GCK2sW9} zO5_1`5~yas^q^a7u&GIxrI diff --git a/efi/testdata/amd64/mockshim_no_sbat.efi.signed.1.1.1 b/efi/testdata/amd64/mockshim_no_sbat.efi.signed.1.1.1 index b66819e89b41f0222aab0b015b9df948ebbf377d..a8b9857462dfd1475c51f581d4ffec1ed920c4be 100644 GIT binary patch delta 289 zcmV++0p9+gFrYAy*agn4000V;K>;VT1OZnS1T!--HIrW!!GCCLJe1VR6aKP8c9!qu z35;${AG@rUuo;f;qO&RwOwq6+k59lxVX8TUAa-U?Cs2(Rv1n;uoV2WP{6W(BqH&>~ zxziSSc>W=>|H(6bX=JNtL$K}juXcB1PH-mB8)j1(08-sdn(Daw;PoyStsR&s7>lC* zJ|8=q5T;x-Wq)%>RKcC2<=qt=e!9m!R}C{(RT)O@la^3gBiCcQq273o z?F3XP4L;s+%7(P3HqYWVm1b`{^g9%)98*$$*Ik0%41h_XmaUC{+!_$V&+>E*;N#I8 nApokwfAm@HCP~BKAp{r!)t02#Xr_dGL|JyHIWc?-JpyDfQ{IHc delta 289 zcmV++0p9+gFrYAy*ah?D000V;K>;VT1OZnS1Tr-=F_T{w!GCURrFwb%MFy7AqQ*UZ6pBZ(DJ2I;P5yXcO&%3@^KBhpdVPSQ$fgF=d108E*=bUS*b3g zKD7{K$=-76QnDLn1n0JN8e857^%vKTR0_}p3X@6XU>D`LfnMRuSo2;VT1Odkv1T!--HIvU6!GCZG=*b=Yt@bw4dOmiM zw6Lcf;2;G2$zBx6TZ`)P!Jc5KRs@8j|K`A+G_eTKse1}-3X zrW6COg<$9GEhJ#S8o=7x9&pf~JA%WO?sZvJrRLBRcwS8|GH+lz8ksVY$h8_JImEd{ nohL!zZnk&vb5ux-r%{FcZ?e(!8JDl%%Lo}Bs)VI09s-uC#=(u7 delta 289 zcmV++0p9+QHIOxs*aa$A000V;K>;VT1Odkv1Tr-=F_X_2!G9G2rUL{Ar4VET63w*1lo4GPI)Xr}D zQ-I*9znrGYt-H2fe-M^FoU)v`Vx@-8lMnvGWqBESI{?ElDJfm7hrL^F?9Ju*8rd%~ nV_6-1TTz7fB`p#gs?pSo3^@iM*-W{Hwja|_i*bQk&;k<_q)LcV diff --git a/efi/testdata/amd64/mockshim_vendor_db.efi.signed.1.1.1 b/efi/testdata/amd64/mockshim_vendor_db.efi.signed.1.1.1 index 0b1a67d480e7a260834fa6d1c05d8038e624ab43..6a4e06a96187c5e32aba4a054f3531fe9527f62b 100644 GIT binary patch delta 289 zcmV++0p9+IIfyxs*afSo000V;K>;VT1OdMp1T!--HIv60!GBs}wurc~wQYI3hj7%p z*uPRr+{k=Hvh=x4o_E)4yH>JjWAr-b;Nl#`mcJO$O)aPke%K(#5&MMD6_!!KF-x&g z=3~DKgbymJD7`aa6yCx`#(nVFJ(Zy_!$|{x4Hg{bzxA2XQT*0fd9rt$B3hKorK&q z(J}tR+{AQgn_M;z(qwO&{!2qd-p1OdnkK5sH_FCwcOi(hs3#lLJatPOD#2tuV3+F2 nti>t(uxXeCfeT@ismur4T7ki*YVB|@WDjlnyr(m(yaJ)niYSqh delta 289 zcmV++0p9+IIfyxs*agD~0RRe;VT1OdMp1Tr-=F_Xs{!GB9gt9n9@+eialiClVn zO6RfL+74xxQEke?b>aqPQT9{R)|}Pcyh5D)Xz|G;XWutt|j= ztdtAKpiw|G-^H7$C@vF+^DHh}_|NUfw3{eDTpjH^?Z3$9i6awF~1s8~m diff --git a/efi/testdata/src/grub/Makefile b/efi/testdata/src/grub/Makefile index 4647a201..ef68df56 100644 --- a/efi/testdata/src/grub/Makefile +++ b/efi/testdata/src/grub/Makefile @@ -13,6 +13,9 @@ NAME ?= mock SBAT_CSV ?= $(TOPDIR)/sbat.csv CPPFLAGS += -DNAME=\"$(NAME)\" +ifneq ($(origin GRUB_PREFIX), undefined) + CPPFLAGS += -DGRUB_PREFIX=\"$(GRUB_PREFIX)\" +endif sbat_data.o: $(SBAT_CSV) $(CC) $(CFLAGS) -x c -c -o $@ /dev/null @@ -20,7 +23,7 @@ sbat_data.o: $(SBAT_CSV) --set-section-flags .sbat=contents,alloc,load,readonly,data \ $@ -OBJS = main_$(ARCH).o data.o +OBJS = main_$(ARCH).o data.o mods.o ifneq ($(origin WITH_SBAT), undefined) OBJS += sbat_data.o endif @@ -32,4 +35,4 @@ $(NAME).so: $(OBJS) $(LD) $(LDFLAGS) $(OBJS) -o $@ %.efi: %.so - $(OBJCOPY) -j .text -j .data -j .reloc -j .sbat --target=efi-app-$(ARCH) $^ $@ + $(OBJCOPY) -j .text -j .data -j .reloc -j .sbat -jmods --target=efi-app-$(ARCH) $^ $@ diff --git a/efi/testdata/src/grub/mods.S b/efi/testdata/src/grub/mods.S new file mode 100644 index 00000000..3686304d --- /dev/null +++ b/efi/testdata/src/grub/mods.S @@ -0,0 +1,17 @@ + .section mods, "a", %progbits + .balignl 8, 0 +.Lgrub_module_info: + .4byte 0x676d696d + .balignl 8, 0 + .8byte .Lgrub_modules_start - .Lgrub_module_info + .8byte .Lgrub_modules_end - .Lgrub_module_info +.Lgrub_modules_start: +#ifdef GRUB_PREFIX +.Lgrub_prefix_start: + .4byte 3 + .4byte .Lgrub_prefix_end - .Lgrub_prefix_start + .ascii GRUB_PREFIX + .byte 0 +.Lgrub_prefix_end: +#endif +.Lgrub_modules_end: diff --git a/tools/make-efi-testdata/apps.go b/tools/make-efi-testdata/apps.go index 8db3a832..d4ef0fb5 100644 --- a/tools/make-efi-testdata/apps.go +++ b/tools/make-efi-testdata/apps.go @@ -108,13 +108,29 @@ func newMockAppData(srcDir, vendorCertDir string, certs map[string][]byte) []moc signCerts: [][]byte{certs["TestUefiSigning1.1.1"]}, filename: "mockshim_no_sbat.efi.signed.1.1.1", }, + { + path: filepath.Join(srcDir, "grub"), + name: "mockgrub", + makeExtraArgs: []string{ + "GRUB_PREFIX=/EFI/ubuntu", + "WITH_SBAT=1", + }, + filename: "mockgrub.efi", + }, + { + path: filepath.Join(srcDir, "grub"), + name: "mockgrub_debian", + makeExtraArgs: []string{ + "GRUB_PREFIX=/EFI/debian", + "WITH_SBAT=1", + }, + filename: "mockgrub_debian.efi", + }, { path: filepath.Join(srcDir, "grub"), - name: "mockgrub1", + name: "mockgrub_no_prefix", makeExtraArgs: []string{"WITH_SBAT=1"}, - signKeys: []string{filepath.Join(srcDir, "keys", "TestShimVendorSigning.1.key")}, - signCerts: [][]byte{certs["TestShimVendorSigning.1"]}, - filename: "mockgrub1.efi.signed.shim.1", + filename: "mockgrub_no_prefix.efi", }, { path: filepath.Join(srcDir, "kernel"), From 421f90ae4cbbd93fb5ee8cbdc1051c74de8e0e00 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 1 Dec 2023 17:25:41 +0000 Subject: [PATCH 4/5] efi: fix an incorrect calculation and add some documentation --- efi/grub.go | 31 ++++++++++++++++++++----------- efi/image_rules_defs.go | 2 +- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/efi/grub.go b/efi/grub.go index 49c9bfa6..85422353 100644 --- a/efi/grub.go +++ b/efi/grub.go @@ -29,28 +29,37 @@ import ( pe "github.com/snapcore/secboot/internal/pe1.14" ) +// grubObjTypePrefix is the module type for a module containing the +// grub prefix. const grubObjTypePrefix uint32 = 3 +// grubModuleHeader is an individual module header type grubModuleHeader struct { - Type uint32 - Size uint32 + Type uint32 // the module type + Size uint32 // the module size, including the header } +// grubModuleMagic represent the bytes that the mods section starts with. const grubModuleMagic uint32 = 0x676d696d +// grubModuleInfo32 is the header at the start of the mods section, for +// 32 bit builds. type grubModuleInfo32 struct { Magic uint32 - Offset uint32 - Size uint32 + Offset uint32 // the offset of the first module, from the start of this header + Size uint32 // the size of all modules, including this header } +// grubModuleInfo64 is the header at the start of the mods section, for +// 64 bit builds. type grubModuleInfo64 struct { Magic uint32 Padding uint32 - Offset uint64 - Size uint64 + Offset uint64 // the offset of the first module, from the start of this header + Size uint64 // the size of all modules, including this header } +// grubModule represents a grub module type grubModule struct { Type uint32 *io.SectionReader @@ -90,13 +99,13 @@ func (h *grubImageHandleImpl) mods() ([]grubModule, error) { if info.Magic != grubModuleMagic { return nil, errors.New("invalid modules magic") } - if info.Size > math.MaxInt64 { + if info.Offset > math.MaxInt64 { return nil, errors.New("invalid modules offset") } - if info.Size > math.MaxInt64 || info.Size < uint64(binary.Size(info)) { + if info.Size > math.MaxInt64 || info.Size < info.Offset { return nil, errors.New("invalid modules size") } - r = io.NewSectionReader(section, int64(info.Offset), int64(info.Size)-int64(binary.Size(info))) + r = io.NewSectionReader(section, int64(info.Offset), int64(info.Size)-int64(info.Offset)) case pe.IMAGE_FILE_MACHINE_ARM, pe.IMAGE_FILE_MACHINE_I386, pe.IMAGE_FILE_MACHINE_RISCV32: var info grubModuleInfo32 if err := binary.Read(section, binary.LittleEndian, &info); err != nil { @@ -105,10 +114,10 @@ func (h *grubImageHandleImpl) mods() ([]grubModule, error) { if info.Magic != grubModuleMagic { return nil, errors.New("invalid module magic") } - if info.Size < uint32(binary.Size(info.Size)) { + if info.Size < info.Offset { return nil, errors.New("invalid modules size") } - r = io.NewSectionReader(section, int64(info.Offset), int64(info.Size)-int64(binary.Size(info))) + r = io.NewSectionReader(section, int64(info.Offset), int64(info.Size)-int64(info.Offset)) default: return nil, fmt.Errorf("unrecognized machine: %d", h.Machine()) } diff --git a/efi/image_rules_defs.go b/efi/image_rules_defs.go index b30ac284..ea517d25 100644 --- a/efi/image_rules_defs.go +++ b/efi/image_rules_defs.go @@ -211,7 +211,7 @@ func makeFallbackImageRules() *imageRules { ), // Ubuntu grub newImageRule( - "grub", + "Ubuntu grub", imageMatchesAll( imageSectionExists("mods"), grubHasPrefix("/EFI/ubuntu"), From 8e2219e4d9b91789b7fcbbfb1531b72bb3d475d5 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 8 Dec 2023 12:12:43 +0000 Subject: [PATCH 5/5] efi: add some documentation and increase test coverage --- efi/grub_test.go | 12 +++ efi/pe.go | 1 + efi/shim_test.go | 2 +- efi/testdata/386/mockgrub.efi | Bin 0 -> 3761 bytes .../amd64/mockgrub1.efi.signed.shim.1 | Bin 5568 -> 0 bytes efi/testdata/amd64/mockshim.efi.signed.1.1.1 | Bin 7304 -> 7304 bytes .../amd64/mockshim.efi.signed.1.2.1+1.1.1 | Bin 9096 -> 9096 bytes .../mockshim_initial_sbat.efi.signed.1.1.1 | Bin 6744 -> 6744 bytes .../amd64/mockshim_no_sbat.efi.signed.1.1.1 | Bin 6176 -> 6176 bytes .../mockshim_no_vendor_cert.efi.signed.1.1.1 | Bin 6800 -> 6800 bytes .../amd64/mockshim_vendor_db.efi.signed.1.1.1 | Bin 7304 -> 7304 bytes efi/testdata/buildenv.yaml | 12 +-- efi/testdata/src/grub/Makefile | 2 +- efi/testdata/src/grub/elf_ia32_efi.lds | 86 ++++++++++++++++++ efi/testdata/src/grub/main_ia32.S | 13 +++ .../src/grub/{main_x86_64.s => main_x86_64.S} | 0 efi/testdata/src/grub/mods.S | 14 ++- tools/make-efi-testdata/apps.go | 35 ++++++- 18 files changed, 165 insertions(+), 12 deletions(-) create mode 100644 efi/testdata/386/mockgrub.efi delete mode 100644 efi/testdata/amd64/mockgrub1.efi.signed.shim.1 create mode 100644 efi/testdata/src/grub/elf_ia32_efi.lds create mode 100644 efi/testdata/src/grub/main_ia32.S rename efi/testdata/src/grub/{main_x86_64.s => main_x86_64.S} (100%) diff --git a/efi/grub_test.go b/efi/grub_test.go index 4b76f741..91f1989a 100644 --- a/efi/grub_test.go +++ b/efi/grub_test.go @@ -53,6 +53,18 @@ func (s *grubSuite) TestGrubImageHandlePrefix2(c *C) { c.Check(prefix, Equals, "/EFI/debian") } +func (s *grubSuite) TestGrubImageHandlePrefix3(c *C) { + image, err := OpenPeImage(NewFileImage("testdata/386/mockgrub.efi")) + c.Assert(err, IsNil) + defer image.Close() + + grubImage := NewGrubImageHandle(image) + + prefix, err := grubImage.Prefix() + c.Check(err, IsNil) + c.Check(prefix, Equals, "/EFI/ubuntu") +} + func (s *grubSuite) TestGrubImageHandlePrefixNone(c *C) { image, err := OpenPeImage(NewFileImage("testdata/amd64/mockgrub_no_prefix.efi")) c.Assert(err, IsNil) diff --git a/efi/pe.go b/efi/pe.go index 057947dc..6091bc45 100644 --- a/efi/pe.go +++ b/efi/pe.go @@ -51,6 +51,7 @@ type peImageHandle interface { // Source returns the image source Source() Image + // Machine is the target machine Machine() uint16 // OpenSection returns a new io.SectionReader for the section with diff --git a/efi/shim_test.go b/efi/shim_test.go index 02a73433..2b140462 100644 --- a/efi/shim_test.go +++ b/efi/shim_test.go @@ -320,7 +320,7 @@ func (s *shimSuite) TestShimImageHandleReadVendorDBEmpty(c *C) { func (s *shimSuite) TestShimImageHandleReadVendorDBNoVendorCert(c *C) { err := s.testShimImageHandleReadVendorDB(c, &testShimImageHandleReadVendorDBData{ - path: "testdata/amd64/mockgrub1.efi.signed.shim.1"}) + path: "testdata/amd64/mockgrub.efi"}) c.Check(err, ErrorMatches, "no .vendor_cert section") } diff --git a/efi/testdata/386/mockgrub.efi b/efi/testdata/386/mockgrub.efi new file mode 100644 index 0000000000000000000000000000000000000000..2e2bdd8bb415c60ad861de8ececf59a748801ae5 GIT binary patch literal 3761 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 delta 294 zcmV+>0oneDIfyxs*aaw|000V;K>;VT1OdAl2{AG_F*7qVH8hjR8o+;vb=nf^lOdRl zX&O5_T0S3vYJDg!O>aF+2y@&`FMR?yILOdisIb&6H?O~VfkO(0BSFXA0MnT)(*z>~ z){VwyS4=lyR~bb7!+S`wC@D6acgf(pXF$%$Ddgh^Cf?u|l?tBTj8Fvq=1vJKCuUFNELj!y6caVC zgNLgP s4%uimn>h(3Et6bg4KzbsnG!~<)w}Riq2QwIgsPQ55Vw_C`;!7#{%~!C2mk;8 diff --git a/efi/testdata/amd64/mockshim.efi.signed.1.2.1+1.1.1 b/efi/testdata/amd64/mockshim.efi.signed.1.2.1+1.1.1 index 11603a720f3e4843ee0fcc883af795bc97388517..ae95cf40efc52db73276f3e49211bb2be8a98299 100644 GIT binary patch delta 572 zcmV-C0>k}?M~Fv|*ab=;0RRe;VT1OdAl2{JJ;GBYzaGBA_K8o+;4V}>%ZaiGIz z_fRTWG!rKz{&@9>$OMVG;tb}Xee1l|Uae61_BwaEWr*qY;*?(kS>-J&9K^H@i-WX8 z5qw||rDD1*AT59&{DXK;S~cA)m=;O?r@W!OC2>l_ysUWKk|4|f0#>hgo1KzTT(7B0 zm8{e8oNVx;n8^>!PCS3+ZSp?Py%jAK;63_e=N>9#doplNwX;mgN$|auINWE~SVVRR zSC-LvLZJ(;>G$a4n*{%7%PGYu5As$IQsr@^Uc5_yf^7AOsN(WeTCN7-7eY<;;W|}m4WCdeo1}RwRoOIBcjPk& zLzPs7^fG|>0&k}?M~Fv|*aaps000V;K>;VT1OdAl2{AG_F*7qVH8hjR8o+-`*K)e{E~dld zBFbS!^cGNuIPt0xQ+!M{>eP4Os~-mD_P;AReEAt#>KiJ!JCk#4fVSxN>y8lnwy)mM ztf-C132l#p)sKJ=v&>eU8*IS))sXtjLZ_~OfA@eE1T)PA4(L$b-Rpt>fG6AEwpMN6 zrjT~mu|zOvjIf%Smui2PMl{2dc+E{|OE%Q2Uc}l7*}-KuCd|x0y#p+meCowqu3VTR z&OuQeYhBcSXC)tlp|awT?VAQn8-Xf>NT2_-F)Tw)D0LtL2>My%Dl@KmATqU?mKl|T@; Km0A0f0$BbcWD-aK diff --git a/efi/testdata/amd64/mockshim_initial_sbat.efi.signed.1.1.1 b/efi/testdata/amd64/mockshim_initial_sbat.efi.signed.1.1.1 index 22e4e1424909fef7a4a194f812f06e2278a6ebda..1d5dbc2435efbd01b02eb474e6decccfe7516501 100644 GIT binary patch delta 293 zcmV+=0owl9G}ttd*#rmy01A^q0VlHr0gD$2GBGhSGcz_aFq4=Vz<-#AC$Q>X{uVIX zefD=icX9w8i}u9^>`tNeXJD)hE>8;bAzg#?%sbuckoXfAZC8lN5uNOjv-{ny6Vv;S zvf%&OhgE+HH#H+{ZS}nG_pj%`C@a=FrL<0j5W9so^@ z?Y&kKc!83ek(J+gK^bB#u--=naTwi$w8}CXLot%v43fY4}gkrPn_wP r-7j*XckXe+&e(PCteE&hLU%;7(y?S=_18dlhR{3@><3+z3OR)2PLXSSy;2M4l)Wj*vn(G}i8tCe zdNmt;Iq0onebFrYAy*aeYA000V;K>;VT1OZbO2{JJ;GBYzaGBA^17QlZ#r!1g)E@^Pu zVd=&h6t#HAd!^5u)JS_#CAAy}QQARTs0!JtwzuNlEpB|kCn>`MmV4k~e(Avm2XnVr#Lw4*!^j_mfh2tS)jW-K|5lhd46fBzTh=a{;k;PTuB1m{06I;g`3Ia50CF zl33vskQeuMNc2hTbp?N@8$y1W8Sh6Bz3$Sp_r&C;qS#_YXv`u`7%oR^DmOB+*w{!5 zC0L&1_=WA*<7nhiSA0~idhp}9Gq!2cv_=a`yoy$C1FuJba4ItQFN}-g?&8OBp&T?H sr9~*$E@1Xx=Fl!1WI+hPY9}JiO;J|?Fb-it1|#17;9#}*MhgO+P?}zcdH?_b delta 294 zcmV+>0onebFrYAy*agn4000V;K>;VT1OZbO2{AG_F*7qVH8hi87QlaKYdn#O&`0gmarL)@1nCR4@}XpB9BkNMq#QsgCKThPbW}~7O`k)U!1h8aQs2i z`J!>5p1IQ&czFIHvj535eQ9K?XhX2=^{;k!V@_};&>Ln`830nV>P{yrZ&n-Hd4G-ZEtNL0a{qvhQd9DcgTJy#7gR#h2S19I{KO7kDj6_b`wS|itE zyP@8Aj_m|gC=EW|amt3Yr#8>xHkD>?JM=pgsvJ{Ne%D=s-VA_Apq8zTf7}`n!q4(_ s4&dX_93cRz!+-Qy?IuaX;2{JU0o9hI*l4DNd_-Axr#Ufv3q1m4Fob@E#Q*>R diff --git a/efi/testdata/amd64/mockshim_no_vendor_cert.efi.signed.1.1.1 b/efi/testdata/amd64/mockshim_no_vendor_cert.efi.signed.1.1.1 index 50295a97de659e65b21346ba81bc17069361431f..1baf8610ebe1b1969feb0e09ef13f1a572a16649 100644 GIT binary patch delta 294 zcmV+>0oneLHIOxs*ahD!000V;K>;VT1OdYr2{JJ;GBYzaGBA_S7{GrPem6pOx-KrQ z`_a1hdX$cOOKnuC_19^!oM!-D9DK}vLv9Khmn$=xE&!)>C8fh8k!Y>a;-Q)=3!<9mlM@U|K?$+_Yv=R5AunQ>795{>i7>nauj;=nmU zL$77XV_l{}SpX%@eIVGM#x1DxWnwJ)=i*-#YBV7yGCDR6nZC<&VC_bsN7VgC7;#QX s5@i-xn$YGZI9(T!eyZcFSvNwg4Xi&_7{p@(tRpZmJAi?w@ZSPOEMv8XX#fBK delta 294 zcmV+>0oneLHIOxs*acTn000V;K>;VT1OdYr2{AG_F*7qVH8hjZ7{GsU3Fyfk{H^vj z(|SI3k+iU<9N-`X{K;Mv$yBcJJ;;3carsdjnA*XZsJyOMX0hu#Fm{i{-w^qWX9 zYpyVN6#t~pxL6Pu64HNDsjWHG$6#aDj4Rg@6f~Ri)<86L?-tE;4UmI~tiXk;t_g sB{{^oM4cx=;cm8f@pDv2jHgkB{BN?+^ck10;L8XZ9;$?;D;@%ts`V3&oB#j- diff --git a/efi/testdata/amd64/mockshim_vendor_db.efi.signed.1.1.1 b/efi/testdata/amd64/mockshim_vendor_db.efi.signed.1.1.1 index 6a4e06a96187c5e32aba4a054f3531fe9527f62b..cf597039c6193323a4ccd0e7fd76618870740c9d 100644 GIT binary patch delta 294 zcmV+>0oneDIfyxs*ahiO000V;K>;VT1OdAl2{JJ;GBYzaGBA_K8o+;0^amjq*WXPq z6YcGVfmYOvouNw7!>3Jxtp+ZnbZ2Ej=4MUx9+4h^+oUO6CRBb}gS>kWa^LMyu`@b#Nr< z?Jp#??}w|Zw>7mTRKfIEv^&DplEZf$%$$+;Vu+L4f%Hp}z!YfjoGjMwZzW}w*3_cP s&A$(unP(LV$`1q1vnr3}-f<>)N>f={DdZ1z8|wlo!)KX}VgLXD delta 294 zcmV+>0oneDIfyxs*afSo000V;K>;VT1OdAl2{AG_F*7qVH8hjR8o+;AVz!95v9)b^ zyN7VpyV$={O5DhNM6&d`O`dnxYr9smXk+v`=iuTT#g@Mq(M>I=3x3!j#}WI4&=r