Skip to content

Commit

Permalink
Add guest package for fetching attestation report via syscall (micros…
Browse files Browse the repository at this point in the history
…oft#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 <maksiman@microsoft.com>
  • Loading branch information
anmaxvl authored Apr 13, 2022
1 parent 5161b37 commit 7d44757
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 40 deletions.
49 changes: 49 additions & 0 deletions 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 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 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 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 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
}
117 changes: 117 additions & 0 deletions tools/snp-report/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//go:build linux
// +build linux

package main

import (
"encoding/hex"
"flag"
"fmt"
"os"

"github.com/Microsoft/hcsshim/internal/tools/snp-report/fake"
"github.com/Microsoft/hcsshim/pkg/amdsevsnp"
)

// verboseReport returns formatted attestation report.
func verboseReport(r amdsevsnp.Report) string {
fieldNameFmt := "%-20s"
pretty := ""
pretty += fmt.Sprintf(fieldNameFmt+"%08x\n", "Version", r.Version)
pretty += fmt.Sprintf(fieldNameFmt+"%08x\n", "GuestSVN", r.GuestSVN)
pretty += fmt.Sprintf(fieldNameFmt+"%016x\n", "Policy", r.Policy)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "FamilyID", r.FamilyID)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "ImageID", r.ImageID)
pretty += fmt.Sprintf(fieldNameFmt+"%08x\n", "VMPL", r.VMPL)
pretty += fmt.Sprintf(fieldNameFmt+"%08x\n", "SignatureAlgo", r.SignatureAlgo)
pretty += fmt.Sprintf(fieldNameFmt+"%016x\n", "PlatformVersion", r.PlatformVersion)
pretty += fmt.Sprintf(fieldNameFmt+"%016x\n", "PlatformInfo", r.PlatformInfo)
pretty += fmt.Sprintf(fieldNameFmt+"%08x\n", "AuthorKeyEn", r.AuthorKeyEn)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "ReportData", r.ReportData)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "Measurement", r.Measurement)
pretty += fmt.Sprintf(fieldNameFmt+"%x\n", "HostData", r.HostData)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "IDKeyDigest", r.IDKeyDigest)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "AuthorKeyDigest", r.AuthorKeyDigest)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "ReportID", r.ReportID)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "ReportIDMA", r.ReportIDMA)
pretty += fmt.Sprintf(fieldNameFmt+"%016x\n", "ReportTCB", r.ReportTCB)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "ChipID", r.ChipID)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "CommittedSVN", r.CommittedSVN)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "CommittedVersion", r.CommittedVersion)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "LaunchSVN", r.LaunchSVN)
pretty += fmt.Sprintf(fieldNameFmt+"%s\n", "Signature", r.Signature)
return pretty
}

func main() {
fakeReportFlag := flag.Bool(
"fake-report",
false,
"If true, don't issue an actual syscall to /dev/sev and return a fake predefined report",
)
hostDataFlag := flag.String(
"host-data",
"",
"Use together with 'fake-report', to set 'HostData' field of fake SNP report.",
)
reportDataFlag := flag.String(
"report-data",
"",
"Report data to use when fetching SNP attestation report",
)
binaryFmtFlag := flag.Bool(
"binary",
false,
"Fetch report in binary format",
)
verbosePrintFlag := flag.Bool(
"verbose",
false,
"Print report in a prettier format",
)

flag.Parse()

var reportBytes []byte
if *reportDataFlag != "" {
var err error
reportBytes, err = hex.DecodeString(*reportDataFlag)
if err != nil {
fmt.Printf("failed to decode report data:%s\n", err)
os.Exit(1)
}
}
if *binaryFmtFlag {
var binaryReport []byte
var err error
if !*fakeReportFlag {
binaryReport, err = fake.FetchRawSNPReport()
} else {
binaryReport, err = amdsevsnp.FetchRawSNPReport(reportBytes)
}
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("%x\n", binaryReport)
os.Exit(0)
}

var report amdsevsnp.Report
var err error
if *fakeReportFlag {
report, err = fake.FetchSNPReport(*hostDataFlag)
} else {
report, err = amdsevsnp.FetchParsedSNPReport(reportBytes)
}
if err != nil {
fmt.Printf("failed to fetch SNP report: %s", err)
os.Exit(1)
}

if !*verbosePrintFlag {
fmt.Printf("%+v\n", report)
} else {
fmt.Println(verboseReport(report))
}
}
Loading

0 comments on commit 7d44757

Please sign in to comment.