Skip to content

Commit

Permalink
Add guest package for fetching attestation report via syscall (#1341)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
anmaxvl authored Apr 13, 2022
1 parent 8af6c33 commit 13ceffd
Show file tree
Hide file tree
Showing 14 changed files with 651 additions and 58 deletions.
21 changes: 19 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
BASE:=base.tar.gz
DEV_BUILD:=0

GO:=go
GO_FLAGS:=-ldflags "-s -w" # strip Go binaries
Expand All @@ -16,6 +17,12 @@ GO_BUILD:=CGO_ENABLED=$(CGO_ENABLED) $(GO) build $(GO_FLAGS) $(GO_FLAGS_EXTRA)

SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST))))

DELTA_TARGET=out/delta.tar.gz

ifeq "$(DEV_BUILD)" "1"
DELTA_TARGET=out/delta-dev.tar.gz
endif

# The link aliases for gcstools
GCS_TOOLS=\
generichook \
Expand Down Expand Up @@ -55,22 +62,32 @@ out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools bin/cmd/ho
tar -zcf $@ -C rootfs .
rm -rf rootfs

# This target includes utilities which may be useful for testing purposes.
out/delta-dev.tar.gz: out/delta.tar.gz bin/internal/tools/snp-report
rm -rf rootfs-dev
mkdir rootfs-dev
tar -xzf out/delta.tar.gz -C rootfs-dev
cp bin/internal/tools/snp-report rootfs-dev/bin/
tar -zcf $@ -C rootfs-dev .
rm -rf rootfs-dev

out/rootfs.tar.gz: out/initrd.img
rm -rf rootfs-conv
mkdir rootfs-conv
gunzip -c out/initrd.img | (cd rootfs-conv && cpio -imd)
tar -zcf $@ -C rootfs-conv .
rm -rf rootfs-conv

out/initrd.img: $(BASE) out/delta.tar.gz $(SRCROOT)/hack/catcpio.sh
$(SRCROOT)/hack/catcpio.sh "$(BASE)" out/delta.tar.gz > out/initrd.img.uncompressed
out/initrd.img: $(BASE) $(DELTA_TARGET) $(SRCROOT)/hack/catcpio.sh
$(SRCROOT)/hack/catcpio.sh "$(BASE)" $(DELTA_TARGET) > out/initrd.img.uncompressed
gzip -c out/initrd.img.uncompressed > $@
rm out/initrd.img.uncompressed

-include deps/cmd/gcs.gomake
-include deps/cmd/gcstools.gomake
-include deps/cmd/hooks/wait-paths.gomake
-include deps/cmd/tar2ext4.gomake
-include deps/internal/tools/snp-report.gomake

# Implicit rule for includes that define Go targets.
%.gomake: $(SRCROOT)/Makefile
Expand Down
49 changes: 49 additions & 0 deletions internal/guest/linux/ioctl.go
Original file line number Diff line number Diff line change
@@ -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
}
34 changes: 34 additions & 0 deletions internal/guest/runtime/hcsv2/hostdata.go
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 9 additions & 0 deletions internal/guest/runtime/hcsv2/uvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
38 changes: 12 additions & 26 deletions internal/guest/storage/devicemapper/devicemapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"unsafe"

"golang.org/x/sys/unix"

"github.com/Microsoft/hcsshim/internal/guest/linux"
)

// CreateFlags modify the operation of CreateDevice
Expand All @@ -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<<linux.IocTypeShift | _DM_IOCTL_SIZE<<linux.IocSizeShift

_DM_READONLY_FLAG = 1 << 0
_DM_SUSPEND_FLAG = 1 << 1
Expand Down Expand Up @@ -132,11 +119,10 @@ func (err *dmError) Error() string {
return "device-mapper " + op + ": " + err.Err.Error()
}

// ioctl issues the specified device-mapper ioctl
func ioctl(f *os.File, code int, data *dmIoctl) error {
_, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(), uintptr(code|_DM_IOCTL_BASE), uintptr(unsafe.Pointer(data)))
if errno != 0 {
return &dmError{Op: code, Err: errno}
// devMapperIoctl issues the specified device-mapper ioctl
func devMapperIoctl(f *os.File, code int, data *dmIoctl) error {
if err := linux.Ioctl(f, code|_DM_IOCTL_BASE, unsafe.Pointer(data)); err != nil {
return &dmError{Op: code, Err: err}
}
return nil
}
Expand All @@ -155,7 +141,7 @@ func openMapper() (f *os.File, err error) {
}()
var d dmIoctl
initIoctl(&d, int(unsafe.Sizeof(d)), "")
err = ioctl(f, _DM_VERSION, &d)
err = devMapperIoctl(f, _DM_VERSION, &d)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -240,7 +226,7 @@ func CreateDevice(name string, flags CreateFlags, targets []Target) (_ string, e
var d dmIoctl
size := int(unsafe.Sizeof(d))
initIoctl(&d, size, name)
err = ioctl(f, _DM_DEV_CREATE, &d)
err = devMapperIoctl(f, _DM_DEV_CREATE, &d)
if err != nil {
return "", err
}
Expand All @@ -256,12 +242,12 @@ func CreateDevice(name string, flags CreateFlags, targets []Target) (_ string, e
if flags&CreateReadOnly != 0 {
di.Flags |= _DM_READONLY_FLAG
}
err = ioctl(f, _DM_TABLE_LOAD, di)
err = devMapperIoctl(f, _DM_TABLE_LOAD, di)
if err != nil {
return "", err
}
initIoctl(&d, size, name)
err = ioctl(f, _DM_DEV_SUSPEND, &d)
err = devMapperIoctl(f, _DM_DEV_SUSPEND, &d)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -306,7 +292,7 @@ func RemoveDevice(name string) (err error) {
func removeDevice(f *os.File, name string) error {
var d dmIoctl
initIoctl(&d, int(unsafe.Sizeof(d)), name)
err := ioctl(f, _DM_DEV_REMOVE, &d)
err := devMapperIoctl(f, _DM_DEV_REMOVE, &d)
if err != nil {
return err
}
Expand Down
57 changes: 57 additions & 0 deletions internal/tools/snp-report/fake/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//go:build linux
// +build linux

package fake

import (
"encoding/hex"
"fmt"

"github.com/Microsoft/hcsshim/pkg/amdsevsnp"
)

const fakeSNPReport = "01000000010000001f00030000000000010000000000000000000000000000000200000000000000000000000000000000000000010000000000000000000028010000000000000000000000000000007ab000a323b3c873f5b81bbe584e7c1a26bcf40dc27e00f8e0d144b1ed2d14f10000000000000000000000000000000000000000000000000000000000000000e29af700e85b39996fa38226d2804b78cad746ffef4477360a61b47874bdecd640f9d32f5ff64a55baad3c545484d9ed28603a3ea835a83bd688b0ec1dcb36b6b8c22412e5b63115b75db8628b989bc598c475ca5f7683e8d351e7e789a1baff19041750567161ad52bf0d152bd76d7c6f313d0a0fd72d0089692c18f521155800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040aea62690b08eb6d680392c9a9b3db56a9b3cc44083b9da31fb88bcfc493407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000028000000000000000000000000000000000000000000000000e6c86796cd44b0bc6b7c0d4fdab33e2807e14b5fc4538b3750921169d97bcf4447c7d3ab2a7c25f74c1641e2885c1011d025cc536f5c9a2504713136c7877f480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003131c0f3e7be5c6e400f22404596e1874381e99d03de45ef8b97eee0a0fa93a4911550330343f14dddbbd6c0db83744f000000000000000000000000000000000000000000000000db07c83c5e6162c2387f3b76cd547672657f6a5df99df98efee7c15349320d83e086c5003ec43050a9b18d1c39dedc340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

// FetchRawSNPReport hex decodes fakeSNPReport.
func FetchRawSNPReport() ([]byte, error) {
return hex.DecodeString(fakeSNPReport)
}

// FetchSNPReport returns amdsev.Report object that corresponds to the decoded
// version of fakeSNPReport. Overrides the resulting report's HostData field
// with provided `hostData`.
func FetchSNPReport(hostData string) (amdsevsnp.Report, error) {
if hostData == "" {
hostData = "28603a3ea835a83bd688b0ec1dcb36b6b8c22412e5b63115b75db8628b989bc5"
}
hdBytes, err := hex.DecodeString(hostData)
if err != nil {
return amdsevsnp.Report{}, fmt.Errorf("failed to decode host data: %w", err)
}
r := amdsevsnp.Report{
Version: 1,
GuestSVN: 1,
Policy: 0x03001f,
FamilyID: "00000000000000000000000000000001",
ImageID: "00000000000000000000000000000001",
VMPL: 0,
SignatureAlgo: 1,
PlatformVersion: 0x2800000000000000,
PlatformInfo: 1,
AuthorKeyEn: 0,
ReportData: "7ab000a323b3c873f5b81bbe584e7c1a26bcf40dc27e00f8e0d144b1ed2d14f10000000000000000000000000000000000000000000000000000000000000000",
Measurement: "e29af700e85b39996fa38226d2804b78cad746ffef4477360a61b47874bdecd640f9d32f5ff64a55baad3c545484d9ed",
HostData: hdBytes[:],
IDKeyDigest: "98c475ca5f7683e8d351e7e789a1baff19041750567161ad52bf0d152bd76d7c6f313d0a0fd72d0089692c18f5211558",
AuthorKeyDigest: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
ReportID: "40aea62690b08eb6d680392c9a9b3db56a9b3cc44083b9da31fb88bcfc493407",
ReportIDMA: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
ReportTCB: 0x2800000000000000,
ChipID: "e6c86796cd44b0bc6b7c0d4fdab33e2807e14b5fc4538b3750921169d97bcf4447c7d3ab2a7c25f74c1641e2885c1011d025cc536f5c9a2504713136c7877f48",
CommittedSVN: "0000000000000000",
CommittedVersion: "0000000000000000",
LaunchSVN: "0000000000000000",
Signature: "3131c0f3e7be5c6e400f22404596e1874381e99d03de45ef8b97eee0a0fa93a4911550330343f14dddbbd6c0db83744f000000000000000000000000000000000000000000000000db07c83c5e6162c2387f3b76cd547672657f6a5df99df98efee7c15349320d83e086c5003ec43050a9b18d1c39dedc340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
}
return r, nil
}
Loading

0 comments on commit 13ceffd

Please sign in to comment.