From 7d44757fbf6c625d03cea15775bf90472f2f6eb1 Mon Sep 17 00:00:00 2001 From: Maksim An Date: Wed, 13 Apr 2022 12:51:38 -0700 Subject: [PATCH] Add guest package for fetching attestation report via syscall (#1341) Add `internal/guest/linux package`, which contains linux ioctl definitions. Devicemapper code is refactored to use the new package. Introduce new `amdsevsnp` package with Introduce ioctl wrappers and structs required to fetch attestation report. Validate that `LaunchData` provided to HCS during UVM boot and `HostData` returned as part of attestation report match. Add utility binary to fetch SNP report and update Makefile to support `DEV_BUILD` parameter, which includes test utilities inside LCOW image. Fake attestation report can be used when testing integrations. Signed-off-by: Maksim An --- guest/linux/ioctl.go | 49 +++++++++ guest/runtime/hcsv2/hostdata.go | 34 ++++++ guest/runtime/hcsv2/uvm.go | 9 ++ guest/storage/devicemapper/devicemapper.go | 38 +++---- tools/snp-report/fake/report.go | 57 ++++++++++ tools/snp-report/main.go | 117 +++++++++++++++++++++ uvm/create_lcow.go | 23 ++-- 7 files changed, 287 insertions(+), 40 deletions(-) create mode 100644 guest/linux/ioctl.go create mode 100644 guest/runtime/hcsv2/hostdata.go create mode 100644 tools/snp-report/fake/report.go create mode 100644 tools/snp-report/main.go diff --git a/guest/linux/ioctl.go b/guest/linux/ioctl.go new file mode 100644 index 0000000000..c87a3da9f5 --- /dev/null +++ b/guest/linux/ioctl.go @@ -0,0 +1,49 @@ +//go:build linux +// +build linux + +// Package linux contains definitions required for making a linux ioctl. +package linux + +import ( + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +// 32 bits to describe an ioctl: +// 0-7: NR (command for a given ioctl type) +// 8-15: TYPE (ioctl type) +// 16-29: SIZE (payload size) +// 30-31: DIR (direction of ioctl, can be: none/write/read/write-read) +const ( + IocWrite = 1 + IocRead = 2 + IocNRBits = 8 + IocTypeBits = 8 + IocSizeBits = 14 + IocDirBits = 2 + + IocNRMask = (1 << IocNRBits) - 1 + IocTypeMask = (1 << IocTypeBits) - 1 + IocSizeMask = (1 << IocSizeBits) - 1 + IocDirMask = (1 << IocDirBits) - 1 + IocTypeShift = IocNRBits + IocSizeShift = IocTypeShift + IocTypeBits + IocDirShift = IocSizeShift + IocSizeBits + IocWRBase = (IocRead | IocWrite) << IocDirShift +) + +// Ioctl makes a syscall described by `command` with data `dataPtr` to device +// driver file `f`. +func Ioctl(f *os.File, command int, dataPtr unsafe.Pointer) error { + if _, _, err := unix.Syscall( + unix.SYS_IOCTL, + f.Fd(), + uintptr(command), + uintptr(dataPtr), + ); err != 0 { + return err + } + return nil +} diff --git a/guest/runtime/hcsv2/hostdata.go b/guest/runtime/hcsv2/hostdata.go new file mode 100644 index 0000000000..562ed006e1 --- /dev/null +++ b/guest/runtime/hcsv2/hostdata.go @@ -0,0 +1,34 @@ +//go:build linux +// +build linux + +package hcsv2 + +import ( + "bytes" + "fmt" + "os" + + "github.com/Microsoft/hcsshim/pkg/amdsevsnp" +) + +// validateHostData fetches SNP report (if applicable) and validates `hostData` against +// HostData set at UVM launch. +func validateHostData(hostData []byte) error { + report, err := amdsevsnp.FetchParsedSNPReport(nil) + if err != nil { + // For non-SNP hardware /dev/sev will not exist + if os.IsNotExist(err) { + return nil + } + return err + } + + if bytes.Compare(hostData, report.HostData) != 0 { + return fmt.Errorf( + "security policy digest %q doesn't match HostData provided at launch %q", + hostData, + report.HostData, + ) + } + return nil +} diff --git a/guest/runtime/hcsv2/uvm.go b/guest/runtime/hcsv2/uvm.go index d5e004fc6a..b1abecc811 100644 --- a/guest/runtime/hcsv2/uvm.go +++ b/guest/runtime/hcsv2/uvm.go @@ -95,6 +95,15 @@ func (h *Host) SetSecurityPolicy(base64Policy string) error { return err } + hostData, err := securitypolicy.NewSecurityPolicyDigest(base64Policy) + if err != nil { + return err + } + + if err := validateHostData(hostData[:]); err != nil { + return err + } + h.securityPolicyEnforcer = p h.securityPolicyEnforcerSet = true diff --git a/guest/storage/devicemapper/devicemapper.go b/guest/storage/devicemapper/devicemapper.go index 3b313284c1..3f9b6797e7 100644 --- a/guest/storage/devicemapper/devicemapper.go +++ b/guest/storage/devicemapper/devicemapper.go @@ -12,6 +12,8 @@ import ( "unsafe" "golang.org/x/sys/unix" + + "github.com/Microsoft/hcsshim/internal/guest/linux" ) // CreateFlags modify the operation of CreateDevice @@ -28,24 +30,9 @@ var ( ) const ( - _IOC_WRITE = 1 - _IOC_READ = 2 - _IOC_NRBITS = 8 - _IOC_TYPEBITS = 8 - _IOC_SIZEBITS = 14 - _IOC_DIRBITS = 2 - - _IOC_NRMASK = ((1 << _IOC_NRBITS) - 1) - _IOC_TYPEMASK = ((1 << _IOC_TYPEBITS) - 1) - _IOC_SIZEMASK = ((1 << _IOC_SIZEBITS) - 1) - _IOC_DIRMASK = ((1 << _IOC_DIRBITS) - 1) - _IOC_TYPESHIFT = (_IOC_NRBITS) - _IOC_SIZESHIFT = (_IOC_TYPESHIFT + _IOC_TYPEBITS) - _IOC_DIRSHIFT = (_IOC_SIZESHIFT + _IOC_SIZEBITS) - _DM_IOCTL = 0xfd _DM_IOCTL_SIZE = 312 - _DM_IOCTL_BASE = (_IOC_READ|_IOC_WRITE)<<_IOC_DIRSHIFT | _DM_IOCTL<<_IOC_TYPESHIFT | _DM_IOCTL_SIZE<<_IOC_SIZESHIFT + _DM_IOCTL_BASE = linux.IocWRBase | _DM_IOCTL<