Skip to content
Closed
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
45 changes: 34 additions & 11 deletions pkg/provisioning/bootguard/bootguard.go
Original file line number Diff line number Diff line change
Expand Up @@ -985,27 +985,50 @@ func (b *BootGuard) IBBsMatchBPMDigest(image []byte) (bool, error) {
}

// ValidateMEAgainstManifests validates during runtime ME configuation with BootGuard KM & BPM manifests
func (b *BootGuard) ValidateMEAgainstManifests(fws *FirmwareStatus6) (bool, error) {
func (b *BootGuard) ValidateMEAgainstManifests(fws *FirmwareStatus) (bool, error) {
switch b.Version {
case bgheader.Version10:
if fws.BPMSVN != uint32(b.VData.BGbpm.BPMSVN) {
if fws.Status6.BPMSVN != uint32(b.VData.BGbpm.BPMSVN) {
return false, fmt.Errorf("bpm svn doesn't match me configuration")
}
if fws.KMSVN != uint32(b.VData.BGkm.KMSVN) {
if fws.Status6.KMSVN != uint32(b.VData.BGkm.KMSVN) {
return false, fmt.Errorf("km svn doesn't match me configuration")
}
if fws.KMID != uint32(b.VData.BGkm.KMID) {
if fws.Status6.KMID != uint32(b.VData.BGkm.KMID) {
return false, fmt.Errorf("km KMID doesn't match me configuration")
}
case bgheader.Version20:
if fws.BPMSVN > uint32(b.VData.CBNTbpm.BPMSVN) {
return false, fmt.Errorf("bpm svn doesn't match me configuration")
}
if fws.KMSVN != uint32(b.VData.CBNTkm.KMSVN) {
return false, fmt.Errorf("km svn doesn't match me configuration")
// We have to make 3 way distinction between ME versions
ver, err := tools.GetMEVersion()
if err != nil {
return false, err
}
if fws.KMID != uint32(b.VData.CBNTkm.KMID) {
return false, fmt.Errorf("km KMID doesn't match me configuration")
switch ver {
case tools.Version16:
if fws.Status6.BPMSVN > uint32(b.VData.CBNTbpm.BPMSVN) {
return false, fmt.Errorf("bpm svn doesn't match me configuration")
}
if fws.Status6.KMSVN != uint32(b.VData.CBNTkm.KMSVN) {
return false, fmt.Errorf("km svn doesn't match me configuration")
}
if fws.Status6.KMID != uint32(b.VData.CBNTkm.KMID) {
return false, fmt.Errorf("km KMID doesn't match me configuration")
}
case tools.Version18, tools.Version21:
// 18.x/21.x do not expose SVNs of BPM and KM, nor KMID.
// We can still check few useful facts
if !fws.Status5.BgACMStatus {
return false, fmt.Errorf("acm is not active")
}
if fws.Status5.ErrorCode != 0 {
return false, fmt.Errorf("bg startup failed")
}
if !fws.Status5.BPMExecStatus {
return false, fmt.Errorf("bpm not executed")
}
if fws.Status5.BgStatus != 0x01 {
return false, fmt.Errorf("bg status is invalid")
}
}
}
return true, nil
Expand Down
73 changes: 71 additions & 2 deletions pkg/provisioning/bootguard/hfsts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,40 @@ import (
// Const Array with HFSTS Offsets
var hfstsOffset = []int{0x40, 0x48, 0x60, 0x64, 0x68, 0x6c}

type FirmwareStatus struct {
// ME 16
Status1 *FirmwareStatus1
Status6 *FirmwareStatus6
// ME 18/21
Status5 *FirmwareStatus5
}

func NewFirmwareStatus(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus, error) {
// ME 16
hwsts1v16, err := getHFSTS1(hw)
if err != nil {
return nil, err
}

hwsts6, err := getHFSTS6(hw)
if err != nil {
return nil, err
}

hwsts5, err := getHFSTS5(hw)
if err != nil {
return nil, err
}

return &FirmwareStatus{
// ME 16
Status1: hwsts1v16,
Status6: hwsts6,
// ME 18/21
Status5: hwsts5,
}, nil
}

type FirmwareStatus1 struct {
WorkingState uint32
MfgMode bool
Expand Down Expand Up @@ -49,7 +83,7 @@ type FirmwareStatus6 struct {
TXTSupported bool
}

func GetHFSTS1(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus1, error) {
func getHFSTS1(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus1, error) {
hfsts1, err := readHFSTSFromPCIConfigSpace(hw, 1)
if err != nil {
return nil, fmt.Errorf("couldn't read HFSTS6 from PCI config space: %v", err)
Expand Down Expand Up @@ -77,7 +111,7 @@ func GetHFSTS1(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus1, error) {
return &firmwareStatus, nil
}

func GetHFSTS6(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus6, error) {
func getHFSTS6(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus6, error) {
hfsts6, err := readHFSTSFromPCIConfigSpace(hw, 6)
if err != nil {
return nil, fmt.Errorf("couldn't read HFSTS6 from PCI config space: %v", err)
Expand Down Expand Up @@ -109,6 +143,41 @@ func GetHFSTS6(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus6, error) {
return &firmwareStatus, nil
}

type FirmwareStatus5 struct {
BgACMStatus bool
VLD bool
RCS bool
ErrorCode uint32
TXTSupported bool
CPUDebugDisabled bool
BSPInitDisabled bool
BPMExecStatus bool
BgStatus uint32
}

// ME 18/21
func getHFSTS5(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus5, error) {
hfsts5, err := readHFSTSFromPCIConfigSpace(hw, 5)
if err != nil {
return nil, fmt.Errorf("couldn't read HFSTS5 from PCI config space: %v", err)
}

firmwareStatus := FirmwareStatus5{}

configSpace := binary.LittleEndian.Uint32(hfsts5)
firmwareStatus.BgACMStatus = (configSpace>>0)&1 != 0
firmwareStatus.VLD = (configSpace>>1)&1 != 0
firmwareStatus.RCS = (configSpace>>2)&1 != 0
firmwareStatus.ErrorCode = (configSpace >> 3) & 31
firmwareStatus.TXTSupported = (configSpace>>17)&1 != 0
firmwareStatus.CPUDebugDisabled = (configSpace>>21)&1 != 0
firmwareStatus.BSPInitDisabled = (configSpace>>22)&1 != 0
firmwareStatus.BPMExecStatus = (configSpace>>23)&1 != 0
firmwareStatus.BgStatus = (configSpace >> 25) & 15

return &firmwareStatus, nil
}

func readHFSTSFromPCIConfigSpace(hw hwapi.LowLevelHardwareInterfaces, offset int) ([]byte, error) {
if offset < 1 || offset > 6 {
return nil, fmt.Errorf("invalid HFSTS offset")
Expand Down
97 changes: 64 additions & 33 deletions pkg/provisioning/bootguard/me.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,46 +55,77 @@ func GetBGInfo(hw hwapi.LowLevelHardwareInterfaces) (*BGInfo, error) {
return &bgi, nil
}

func StrictSaneBootGuardProvisioning(v bgheader.BootGuardVersion, fws *FirmwareStatus6, bgi *BGInfo) (bool, error) {
if fws.ErrorEnforcementPolicy != EnforcementPolicyShutdownImmediately {
func StrictSaneBootGuardProvisioning(v bgheader.BootGuardVersion, fws *FirmwareStatus, bgi *BGInfo) (bool, error) {
if fws.Status6.ErrorEnforcementPolicy != EnforcementPolicyShutdownImmediately {
return false, fmt.Errorf("enforcement policy isn't set to immediate shutdown")
}

return SaneMEBootGuardProvisioning(v, fws, bgi)
}

// SaneMEBootGuardProvisioning validates during runtime ME bootguard provisioning
func SaneMEBootGuardProvisioning(v bgheader.BootGuardVersion, fws *FirmwareStatus6, bgi *BGInfo) (bool, error) {
if fws.BypassBootPolicy {
return false, fmt.Errorf("bypass boot policy is active")
}
if fws.BootPolicyInvalid {
return false, fmt.Errorf("boot policy is invalid")
}
if !fws.FPFLock {
return false, fmt.Errorf("FPF isn't locked")
}
if fws.ErrorEnforcementPolicy == EnforcementPolicyDoNothing ||
fws.ErrorEnforcementPolicy == EnforcementPolicyShutdownSomehow {
return false, fmt.Errorf("enforcement policy is lazy and doesn't stop boot process")
}
if !fws.ProtectBIOSEnvironment {
return false, fmt.Errorf("protected bios enviroment is disabled")
}
if v == bgheader.Version20 && !bgi.ForceAnchorBoot {
return false, fmt.Errorf("force anchor boot is disabled")
}
if !bgi.Verified {
return false, fmt.Errorf("verified boot is disabled, measured boot only may be possible but isn't supported by Intel officially")
}
if bgi.ModuleRevoked {
return false, fmt.Errorf("one of the the ACM, BPM and KM may be revoked")
}
if fws.BootGuardDisable {
return false, fmt.Errorf("boot guard is disabled")
}
if !bgi.BootGuardCapability {
return false, fmt.Errorf("missing boot guard microcode updates in FIT")
func SaneMEBootGuardProvisioning(v bgheader.BootGuardVersion, fws *FirmwareStatus, bgi *BGInfo) (bool, error) {
ver, err := tools.GetMEVersion()
if err != nil {
return false, err
}

switch ver {
case tools.Version16:
if fws.Status6.BypassBootPolicy {
return false, fmt.Errorf("bypass boot policy is active")
}
if fws.Status6.BootPolicyInvalid {
return false, fmt.Errorf("boot policy is invalid")
}
if !fws.Status6.FPFLock {
return false, fmt.Errorf("FPF isn't locked")
}
if fws.Status6.ErrorEnforcementPolicy == EnforcementPolicyDoNothing ||
fws.Status6.ErrorEnforcementPolicy == EnforcementPolicyShutdownSomehow {
return false, fmt.Errorf("enforcement policy is lazy and doesn't stop boot process")
}
if !fws.Status6.ProtectBIOSEnvironment {
return false, fmt.Errorf("protected bios enviroment is disabled")
}
if v == bgheader.Version20 && !bgi.ForceAnchorBoot {
return false, fmt.Errorf("force anchor boot is disabled")
}
if !bgi.Verified {
return false, fmt.Errorf("verified boot is disabled, measured boot only may be possible but isn't supported by Intel officially")
}
if bgi.ModuleRevoked {
return false, fmt.Errorf("one of the the ACM, BPM and KM may be revoked")
}
if fws.Status6.BootGuardDisable {
return false, fmt.Errorf("boot guard is disabled")
}
if !bgi.BootGuardCapability {
return false, fmt.Errorf("missing boot guard microcode updates in FIT")
}
case tools.Version18, tools.Version21:
if !fws.Status6.FPFLock {
return false, fmt.Errorf("FPF is not locked")
}
if fws.Status1.MfgMode {
return false, fmt.Errorf("debug mode is enabled")
}
if !fws.Status5.VLD {
return false, fmt.Errorf("bits that follow are invalid")
}
if fws.Status5.RCS {
return false, fmt.Errorf("RCS does not come from ACM")
}
if !fws.Status5.CPUDebugDisabled {
return false, fmt.Errorf("cpu debug is enabled")
}
if fws.Status1.WorkingState != 0x05 {
return false, fmt.Errorf("invalid working state")
}
if fws.Status1.OperatingMode != 0 {
return false, fmt.Errorf("invalid operating mode")
}

}
return true, nil
}
Expand Down
52 changes: 52 additions & 0 deletions pkg/tools/me.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package tools

import (
"fmt"
"strconv"
"strings"

"github.com/prometheus/procfs/sysfs"
)

type MEVersion uint8

const (
Version16 MEVersion = 16
Version18 MEVersion = 18
Version21 MEVersion = 21
)

func GetMEVersion() (MEVersion, error) {
fs, err := sysfs.NewFS("/sys")
if err != nil {
return 0, err
}

mei, err := fs.MEIClass()
if err != nil {
return 0, err
}

// There are always 4 lines in fw_version (no clue why) exposed in sysfs.
// So lets take the first line and then look for what is interesting for us,
// i.e. 0:N where N is one of the defined MEVersion's
fline := strings.Split(*mei.FWVersion, "\n")
felem := strings.Split(fline[0], ".")
pref := strings.Split(felem[0], ":")

ver, err := strconv.Atoi(pref[1])
if err != nil {
return 0, err
}

switch ver {
case 16:
return Version16, nil
case 18:
return Version18, nil
case 21:
return Version21, nil
}

return 0, fmt.Errorf("unknown ME version")
}
Loading