diff --git a/pkg/provisioning/bootguard/bootguard.go b/pkg/provisioning/bootguard/bootguard.go index d25db6ea..8a2384e4 100644 --- a/pkg/provisioning/bootguard/bootguard.go +++ b/pkg/provisioning/bootguard/bootguard.go @@ -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 diff --git a/pkg/provisioning/bootguard/hfsts.go b/pkg/provisioning/bootguard/hfsts.go index 3c2f0a13..581db760 100644 --- a/pkg/provisioning/bootguard/hfsts.go +++ b/pkg/provisioning/bootguard/hfsts.go @@ -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 @@ -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) @@ -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) @@ -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") diff --git a/pkg/provisioning/bootguard/me.go b/pkg/provisioning/bootguard/me.go index d2cc25d7..e9d86b5d 100644 --- a/pkg/provisioning/bootguard/me.go +++ b/pkg/provisioning/bootguard/me.go @@ -55,8 +55,8 @@ 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") } @@ -64,37 +64,68 @@ func StrictSaneBootGuardProvisioning(v bgheader.BootGuardVersion, fws *FirmwareS } // 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 } diff --git a/pkg/tools/me.go b/pkg/tools/me.go new file mode 100644 index 00000000..5acd3b19 --- /dev/null +++ b/pkg/tools/me.go @@ -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") +}