-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
preinstall: Add checks for platform firmware protections #316
preinstall: Add checks for platform firmware protections #316
Conversation
8b04c23
to
fc155fd
Compare
This adds checks for how the platform firmware is protected, with specific tests for whether CPU silicon debugging features are enabled or not locked off, whether a firmware debugging endpoint (such as UDK) is enabled, whether the kernel IOMMU is available, whether the firmware indicates that DMA protection was disabled at some point, and whether the firmware is protected by a hardware root-of-trust (ie, keys fused into the silicon during manufacturing). Some of these checks are based on the HSI checks in fwupd with some minor adaptions, particularly for the BootGuard checks where there are some tests in fwupd that disagree with other publicly available documentation which better aligns with hardware that is assumed to be good (note that Intel do not publicly document how any of this works). This has a significant limitation today - it only works on Intel based systems. AMD support will happen when we figure out how the platform firmware is protected on AMD platforms with the dedicated PSP. There are some other limitations, eg, whilst we detect that the IOMMU functionality in the kernel is enabled, we don't check that external facing DMA capable ports or user accessible internal DMA capable ports are protected by one of the domains (and this feature could be made optional if there aren't external facing or user-accessible internal ports that aren't capable of DMA). This does perform a check of the secure boot PCR for an event that is not documented anywhere by the TCG, isn't in EDK2 or any of Microsoft's public Project Mu repositories, but is a Windows requirement (measuring a EV_EFI_ACTION event with the string "DMA Protection Disabled" to PCR7 if DMA protection is disabled by any settings in the firmware). Note that strings associated with EV_EFI_ACTION events aren't meant to be NULL terminated, although the one on my Dell XPS15 is so the code handles the case where the event data is NULL terminated too.
fc155fd
to
de39165
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks reasonable to me, did Mate review this?
) | ||
|
||
func checkPlatformFirmwareProtections(env HostEnvironment, log *tcglog.Log) (result firmwareProtectionResultFlags, err error) { | ||
return 0, errors.New("checking platform firmware protections is not implemented on this platform") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't the not implemented error be recognisable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a new error type UnsupportedPlatformError
for this.
result |= platformFirmwareProtectionsTPMLocality3IsProtected | ||
} | ||
case cpuVendorAMD: | ||
return 0, errors.New("TODO: checking platform firmware protections is not yet implemented for AMD") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous response.
I've addressed the minor comments about distinguishing errors, so I've popped it back in your queue @pedronis. We can wait for some comments from Mate, although a lot of this was developed in consultation with him anyway (particularly the BootGuard bits - thanks Mate!) so none of what is in this PR should be too much of a surprise. |
This only had a single value. Return a protected localities result instead.
I pushed a small change to this to remove the flags returned from the main entry point, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that I haven't tested this code on my own hardware, this review is only based on reading it.
Left a couple notes, but overall seems good to me and implements what Chris was telling me about earlier.
// On Intel devices, the domains are defined by the DMAR ACPI table. The check is quite | ||
// simple, and based on the fwupd HSI checks. | ||
// XXX: Figure out whether this is genuinely sufficient, eg: | ||
// - Should we only mandate this if there are externally facing ports, or internal ports |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we believe that attacks against TPM FDE involving physical access are relevant to our threat model, then we must by definition consider DMA via internal ports a problem.
Based on our advocacy for BootGuard, this very much seems to be the case, replacing the contents of an SPI flash device isn't really a lower barrier that needs more protection than DMA via an internal PCIe connector.
return &NoHardwareRootOfTrustError{errors.New("BootGuard is disabled")} | ||
} | ||
|
||
// Verify the BootGuard profile |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BootGuard FVME profile is okay from a security perspective so +1 on that.
I think supporting measured boot would be nice too, but this is okay, and most devices use this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about adding support for a measured boot profile as well (the consequence of this is that we bring PCR0 into the profile), but perhaps only on firmware TPMs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In light of the GPIO problem and Intel doing nothing about it software-wise, yes that would make sense to me, at least temporarily.
I have heard (unverified) claims that some very new systems might have TPM reset wired in such a way that it isn't shared with GPIO.
The usual hardware attacks against discrete TPMs exist as always, but those are not mitigated by verified boot either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. If we get a generation of devices with discrete TPMs with reset signals that are tied to the host CPU reset and can't be controlled via GPIO, then we can add extra checks in the future to turn off the reset mitigation on those platforms (which is just adding PCR0 to the profile if TPM2_Startup came from locality 3 or if there was a H-CRTM event sequence, which requires locality 4 access, both of which change the initial value of PCR0).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, some small comments and a comment about errors/error definitions vs future handling needs for them
@@ -98,7 +98,19 @@ func checkPlatformFirmwareProtections(env internal_efi.HostEnvironment, log *tcg | |||
return 0, fmt.Errorf("encountered an error when determining platform firmware protections using Intel MEI: %w", err) | |||
} | |||
if amd64Env.HasCPUIDFeature(cpuid.SMX) { | |||
// The Intel TXT spec says that locality 3 is only available to ACMs | |||
// The Intel TXT spec says that locality 4 is basically only avilable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: available
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've fixed this now.
// has access to locality 3. Access to this is meant to be locked at the | ||
// hardware level before running non-Intel code, although I'm not sure if | ||
// this is only relevant in the D-CRTM case where the SINIT ACM has access | ||
//to locality 3, and it locks access to it, leaving access to localities 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strange formatting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this one was already fixed in a recent push I made
internal_efi "github.com/snapcore/secboot/internal/efi" | ||
) | ||
|
||
// NoHardwareRootOfTrustError is returned wrapped from [RunChecks] if the platform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as mentioned in London my preference would be for all the errors that RunChecks can return to live in a single errors.go file as we will have to work out at a later point a mechanism to expose them to clients of snapd, and we would also like an easy way to point also people to the full list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved errors to a new errors.go now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you
This adds checks for how the platform firmware is protected, with
specific tests for whether CPU silicon debugging features are enabled or
not locked off, whether a firmware debugging endpoint (such as UDK) is
enabled, whether the kernel IOMMU is available, whether the firmware
indicates that DMA protection was disabled at some point, and whether
the firmware is protected by a hardware root-of-trust (ie, keys fused
into the silicon during manufacturing).
The main entry point to this is
checkPlatformFirmwareProtections
.There is an implementation for amd64 go builds (x86-64) and then a null
implementation for everything that is !amd64 which just returns an error.
Some of these checks are based on the HSI checks in fwupd with some minor
adaptions, particularly for the BootGuard checks where there are some tests
in fwupd that disagree with other publicly available documentation which
better aligns with hardware that is assumed to be good (note that Intel do
not publicly document how any of this works).
This has a significant limitation today - it only works on Intel based
systems. AMD support will happen when we figure out how the platform
firmware is protected on AMD platforms with the dedicated PSP.
There are some other limitations, eg, whilst we detect that the IOMMU
functionality in the kernel is enabled, we don't check that external
facing DMA capable ports or user accessible internal DMA capable ports
are protected by one of the domains (and this feature could be made
optional if there aren't external facing or user-accessible internal
ports that are capable of DMA). Microsoft say that externally accessible
ports can be identified using ACPI tables (https://learn.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports#identifying-externally-exposed-pcie-root-ports), but we don't
do anything like this yet. I'm going to create a follow-on epic to expand support
for this feature, and this check will likely end up in there.
This does perform a check of the secure boot PCR for an event that is
not documented anywhere by the TCG, isn't in EDK2 or any of Microsoft's
public Project Mu repositories, but is a Windows requirement (measuring
a
EV_EFI_ACTION
event with the string "DMA Protection Disabled" to PCR7if DMA protection is disabled by any settings in the firmware). Note
that strings associated with
EV_EFI_ACTION
events aren't meant to beNULL terminated, although the one on my Dell XPS15 is so the code
handles the case where the event data is NULL terminated too. I don't
know if this is a Dell bug or if Microsoft generally expects this to be a NULL
terminated, but this contradicts the TCG spec for this event type. The only
places where NULL terminated strings are used are for
EV_S_CRTM_CONTENTS
when measured by a H-CRTM event, and a NULL terminated UCS2 string in
the data associated with
EV_S_CRTM_VERSION
events (yay for consistency!)