Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

secboot: check legacy path for keyring when reading the primary key #14966

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions osutil/disks/disks_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ func SectorSize(devname string) (uint64, error) {
func filesystemTypeForPartition(devname string) (string, error) {
return "", osutil.ErrDarwin
}

func Devlinks(node string) ([]string, error) {
return []string{}, osutil.ErrDarwin
}
13 changes: 13 additions & 0 deletions osutil/disks/disks_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1035,3 +1035,16 @@ func filesystemTypeForPartition(devname string) (string, error) {

return props["ID_FS_TYPE"], nil
}

// Devlinks returns all the /dev symlinks for a node
func Devlinks(node string) ([]string, error) {
props, err := udevPropertiesForName(node)
if err != nil && props == nil {
return []string{}, fmt.Errorf("cannot process udev properties: %v", err)
}
devlinks := props["DEVLINKS"]
if devlinks == "" {
return []string{}, fmt.Errorf("cannot get required udev DEVLINKS property")
}
return strings.Split(devlinks," "), nil
}
30 changes: 30 additions & 0 deletions osutil/disks/disks_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2050,3 +2050,33 @@ func (s *diskSuite) TestFilesystemUUID(c *C) {
_, err = disks.FilesystemUUID("/dev/vda6")
c.Check(err, ErrorMatches, `cannot process udev properties: some error`)
}

func (s *diskSuite) TestDevlinks(c *C) {
restore := disks.MockUdevPropertiesForDevice(func(typeOpt, dev string) (map[string]string, error) {
c.Assert(typeOpt, Equals, "--name")
switch dev {
case "/dev/some/main/link":
return map[string]string{
"DEVLINKS": "/dev/some/other/link /dev/yet/another/link",
}, nil
case "/dev/no/links":
return map[string]string{}, nil
case "/dev/some/error":
return nil, fmt.Errorf("some error")
default:
c.Errorf("unexpected udev device properties requested: %s", dev)
return nil, fmt.Errorf("unexpected udev device: %s", dev)
}
})
defer restore()

links, err := disks.Devlinks("/dev/some/main/link")
c.Assert(err, IsNil)
c.Check(links, DeepEquals, []string{"/dev/some/other/link", "/dev/yet/another/link"})

_, err = disks.Devlinks("/dev/no/links")
c.Check(err, ErrorMatches, `cannot get required udev DEVLINKS property`)

_, err = disks.Devlinks("/dev/some/error")
c.Check(err, ErrorMatches, `cannot process udev properties: some error`)
}
16 changes: 16 additions & 0 deletions secboot/export_sb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,19 @@ func MockTpmGetCapabilityHandles(f func(tpm *sb_tpm2.Connection, firstHandle tpm
tpmGetCapabilityHandles = old
}
}

func MockSbGetPrimaryKeyFromKernel(f func(prefix string, devicePath string, remove bool) (sb.PrimaryKey, error)) (restore func()) {
old := sbGetPrimaryKeyFromKernel
sbGetPrimaryKeyFromKernel = f
return func() {
sbGetPrimaryKeyFromKernel = old
}
}

func MockDisksDevlinks(f func(node string) ([]string, error)) (restore func()) {
old := disksDevlinks
disksDevlinks = f
return func() {
disksDevlinks = old
}
}
48 changes: 42 additions & 6 deletions secboot/secboot_sb.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"fmt"
"os"
"path/filepath"
"strings"

sb "github.com/snapcore/secboot"
sb_plainkey "github.com/snapcore/secboot/plainkey"
Expand All @@ -52,6 +53,8 @@
sbRenameLUKS2ContainerKey = sb.RenameLUKS2ContainerKey
sbNewLUKS2KeyDataReader = sbNewLUKS2KeyDataReaderImpl
sbSetProtectorKeys = sb_plainkey.SetProtectorKeys
sbGetPrimaryKeyFromKernel = sb.GetPrimaryKeyFromKernel
disksDevlinks = disks.Devlinks
)

func init() {
Expand Down Expand Up @@ -410,10 +413,41 @@
return nil
}

// FIXME: add tests
func GetPrimaryKeyDigest(devicePath string, alg crypto.Hash) (salt []byte, digest []byte, err error) {
func findPrimaryKey(devicePath string) ([]byte, error) {
const remove = false
p, err := sb.GetPrimaryKeyFromKernel(keyringPrefix, devicePath, remove)
p, err := sbGetPrimaryKeyFromKernel(keyringPrefix, devicePath, remove)
if err == nil {
return p, nil
}
if !errors.Is(err, sb.ErrKernelKeyNotFound) {
return nil, err
}

Check warning on line 424 in secboot/secboot_sb.go

View check run for this annotation

Codecov / codecov/patch

secboot/secboot_sb.go#L423-L424

Added lines #L423 - L424 were not covered by tests

// Old kernels will use "by-partuuid" symlinks. So let's
// look at all the symlinks of the device.
devlinks, errDevlinks := disksDevlinks(devicePath)
if errDevlinks != nil {
return nil, err
}

Check warning on line 431 in secboot/secboot_sb.go

View check run for this annotation

Codecov / codecov/patch

secboot/secboot_sb.go#L430-L431

Added lines #L430 - L431 were not covered by tests
var errDevlink error
for _, devlink := range devlinks {
if !strings.HasPrefix(devlink, "/dev/disk/by-partuuid/") {
continue
}
p, errDevlink = sbGetPrimaryKeyFromKernel(keyringPrefix, devlink, remove)
if errDevlink == nil {
return p, nil
}
}
return nil, err

Check warning on line 442 in secboot/secboot_sb.go

View check run for this annotation

Codecov / codecov/patch

secboot/secboot_sb.go#L442

Added line #L442 was not covered by tests
}

// GetPrimaryKeyDigest retrieve the primary key for a disk from the
// keyring and returns its digest. If the path given does not match
// the keyring, then it will look for symlink in /dev/disk/by-partuuid
// for that device.
func GetPrimaryKeyDigest(devicePath string, alg crypto.Hash) (salt []byte, digest []byte, err error) {
p, err := findPrimaryKey(devicePath)
if err != nil {
if errors.Is(err, sb.ErrKernelKeyNotFound) {
return nil, nil, ErrKernelKeyNotFound
Expand All @@ -431,10 +465,12 @@
return saltArray[:], h.Sum(nil), nil
}

// FIXME: add tests
// VerifyPrimaryKeyDigest retrieve the primary key for a disk from the
// keyring and verifies its digest. If the path given does not match
// the keyring, then it will look for symlink in /dev/disk/by-partuuid
// for that device.
func VerifyPrimaryKeyDigest(devicePath string, alg crypto.Hash, salt []byte, digest []byte) (bool, error) {
const remove = false
p, err := sb.GetPrimaryKeyFromKernel(keyringPrefix, devicePath, remove)
p, err := findPrimaryKey(devicePath)
if err != nil {
if errors.Is(err, sb.ErrKernelKeyNotFound) {
return false, ErrKernelKeyNotFound
Expand Down
76 changes: 76 additions & 0 deletions secboot/secboot_sb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package secboot_test

import (
"bytes"
"crypto"
"crypto/rand"
"encoding/base64"
"encoding/binary"
Expand Down Expand Up @@ -3423,3 +3424,78 @@ func (s *secbootSuite) TestFindFreeHandleNoneFree(c *C) {
_, err := secboot.FindFreeHandle()
c.Assert(err, ErrorMatches, `no free handle on TPM`)
}

func (s *secbootSuite) TestGetPrimaryKeyDigest(c *C) {
defer secboot.MockDisksDevlinks(func(node string) ([]string, error) {
c.Errorf("unexpected call")
return nil, errors.New("unexpected call")
})()
defer secboot.MockSbGetPrimaryKeyFromKernel(func(prefix string, devicePath string, remove bool) (sb.PrimaryKey, error) {
c.Check(prefix, Equals, "ubuntu-fde")
c.Check(devicePath, Equals, "/dev/test/device")
c.Check(remove, Equals, false)
return []byte{0, 1, 2, 3}, nil
})()
salt, digest, err := secboot.GetPrimaryKeyDigest("/dev/test/device", crypto.SHA256)
c.Assert(err, IsNil)
defer secboot.MockSbGetPrimaryKeyFromKernel(func(prefix string, devicePath string, remove bool) (sb.PrimaryKey, error) {
c.Check(prefix, Equals, "ubuntu-fde")
c.Check(devicePath, Equals, "/dev/other/device")
c.Check(remove, Equals, false)
return []byte{0, 1, 2, 3}, nil
})()
matches, err := secboot.VerifyPrimaryKeyDigest("/dev/other/device", crypto.SHA256, salt, digest)
c.Assert(err, IsNil)
c.Check(matches, Equals, true)
defer secboot.MockSbGetPrimaryKeyFromKernel(func(prefix string, devicePath string, remove bool) (sb.PrimaryKey, error) {
c.Check(prefix, Equals, "ubuntu-fde")
c.Check(devicePath, Equals, "/dev/not/matching")
c.Check(remove, Equals, false)
return []byte{8, 8, 8, 8}, nil
})()
matches, err = secboot.VerifyPrimaryKeyDigest("/dev/not/matching", crypto.SHA256, salt, digest)
c.Assert(err, IsNil)
c.Check(matches, Equals, false)
}

func (s *secbootSuite) TestGetPrimaryKeyDigestFallbackDevPath(c *C) {
defer secboot.MockDisksDevlinks(func(node string) ([]string, error) {
c.Check(node, Equals, "/dev/test/device")
return []string{
"/dev/link/to/ignore",
"/dev/test/device",
"/dev/disk/by-partuuid/a9456fe6-9850-41ce-b2ad-cf9b43a34286",
}, nil
})()
defer secboot.MockSbGetPrimaryKeyFromKernel(func(prefix string, devicePath string, remove bool) (sb.PrimaryKey, error) {
c.Check(prefix, Equals, "ubuntu-fde")
c.Check(remove, Equals, false)
if devicePath == "/dev/test/device" {
return nil, sb.ErrKernelKeyNotFound
}
c.Check(devicePath, Equals, "/dev/disk/by-partuuid/a9456fe6-9850-41ce-b2ad-cf9b43a34286")
return []byte{0, 1, 2, 3}, nil
})()
salt, digest, err := secboot.GetPrimaryKeyDigest("/dev/test/device", crypto.SHA256)
c.Assert(err, IsNil)
defer secboot.MockDisksDevlinks(func(node string) ([]string, error) {
c.Check(node, Equals, "/dev/other/device")
return []string{
"/dev/link/to/ignore",
"/dev/other/device",
"/dev/disk/by-partuuid/58c54e4e-1e86-4bda-a51c-af50ff8447ab",
}, nil
})()
defer secboot.MockSbGetPrimaryKeyFromKernel(func(prefix string, devicePath string, remove bool) (sb.PrimaryKey, error) {
c.Check(prefix, Equals, "ubuntu-fde")
c.Check(remove, Equals, false)
if devicePath == "/dev/other/device" {
return nil, sb.ErrKernelKeyNotFound
}
c.Check(devicePath, Equals, "/dev/disk/by-partuuid/58c54e4e-1e86-4bda-a51c-af50ff8447ab")
return []byte{0, 1, 2, 3}, nil
})()
matches, err := secboot.VerifyPrimaryKeyDigest("/dev/other/device", crypto.SHA256, salt, digest)
c.Assert(err, IsNil)
c.Check(matches, Equals, true)
}
Loading