[PATCH] efi_loader: explicitly return EFI_UNSUPPORTED for TCG 1.0 compatibility

Heinrich Schuchardt xypron.glpk at gmx.de
Wed May 31 19:42:53 CEST 2023


On 5/31/23 18:41, Stuart Yoder wrote:
>
>
> On 5/31/23 2:48 AM, Ilias Apalodimas wrote:
>> Hi Stuart,
>>
>> On Wed, 31 May 2023 at 00:20, Stuart Yoder <stuart.yoder at arm.com> wrote:
>>>
>>>
>>>
>>> On 5/30/23 1:39 AM, Ilias Apalodimas wrote:
>>>> In the EFI TCG spec EFI_TCG2_BOOT_SERVICE_CAPABILITY struct is
>>>> versioned -- there are 1.0 and 1.1 versions of that struct.
>>>> The spec [0] describes:
>>>> "Version of the EFI_TCG2_BOOT_SERVICE_CAPABILITY
>>>> structure itself. For this version of the protocol, the Major version
>>>> SHALL be set to 1 and the Minor version SHALL be set to 1."
>>>> which is what we currently support.
>>>>
>>>> The SCT tests perfromed By Arms SIE(Security interface extensions) [1]
>>>> perform a check for clients supporting the older 1.0 version of the
>>>> spec (Test 30.1.1.4). Given than this spec is 7 years old,  there
>>>> should
>>>> be no need for the older 1.0 version support.  Instead of returning
>>>> EFI_BUFFER_TOO_SMALLL switch to EFI_UNSUPPORTED which is more
>>>> appropriate.  It's worth noting that the spec doesn't explicitly
>>>> describe the return value at the moment.

Please, open a bug report for the SCT. It should check the value of
ProtocolCapability.ProtocolVersion returned by GetCapability() and
provide code paths for 1.0 and 1.1.

>>>>
>>>> [0]
>>>> https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf
>>>> [1]
>>>> https://github.com/stuyod01/edk2-test/blob/master/uefi-sct/Doc/UEFI-SCT-Case-Spec/30_Protocols_TCG2_Test.md
>>>>
>>>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
>>>> ---
>>>>
>>>> Heinrich,  Stuart is investigating the chance of the spec getting
>>>> updated
>>>> adding EFI_UNSUPPORTED.  In any case I think the patch should be
>>>> aplied since
>>>> the new return code makes more sense.  If for some reason the spec
>>>> change is
>>>> rejected, I can go back and add support for 1.0 structure versions.
>>>>
>>>>    lib/efi_loader/efi_tcg2.c | 2 +-
>>>>    1 file changed, 1 insertion(+), 1 deletion(-)
>>>>
>>>> diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c
>>>> index a83ae7a46cf3..220c442bdf93 100644
>>>> --- a/lib/efi_loader/efi_tcg2.c
>>>> +++ b/lib/efi_loader/efi_tcg2.c
>>>> @@ -750,7 +750,7 @@ efi_tcg2_get_capability(struct efi_tcg2_protocol
>>>> *this,
>>>>
>>>>        if (capability->size < sizeof(*capability)) {
>>>>                capability->size = sizeof(*capability);
>>>> -             efi_ret = EFI_BUFFER_TOO_SMALL;
>>>> +             efi_ret = EFI_UNSUPPORTED;

This is contradicts the standard. You MUST return EFI_BUFFER_TOO_SMALL.
It is the clients obligation to check StructureVersion and ProtocolVersion.

>>>>                goto out;
>>>>        }
>>>
>>> Hi Ilias,
>>>
>>> I don't think this is the right fix.
>>>
>>> The struct looks like this:
>>>
>>> struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY {
>>>     UINT8 Size;
>>>     EFI_TCG2_VERSION StructureVersion;
>>>     EFI_TCG2_VERSION ProtocolVersion;
>>>     EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap
>>>     EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs
>>>     BOOLEAN TPMPresentFlag
>>>     UINT16 MaxCommandSize
>>>     UINT16 MaxResponseSize
>>>     UINT32 ManufacturerID
>>>     UINT32 NumberOfPcrBanks
>>>     EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks
>>> };
>>>
>>> The intent in the TCG spec is for the caller to be able to
>>> determine the size of the struct by passing in a small
>>> buffer (e.g. 1 byte buffer), and then the firmware
>>> should return sizeof(EFI_TCG2_BOOT_SERVICE_CAPABILITY) in the
>>> 1-byte "Size" field.  And the return value should be
>>> EFI_BUFFER_TOO_SMALL as per the spec.
>>>
>>> In order to detect a 1.0 client, which you don't want to support,
>>> I think you need a _new_ check that is something like this:
>>>
>>> // detect 1.0 client
>>> if (capability->size < sizeof(*capability) &&
>>>       capability->size >= offsetof(EFI_TCG2_BOOT_SERVICE_CAPABILITY,
>>> NumberOfPcrBanks)) {
>>>       efi_ret = EFI_UNSUPPORTED;
>>>       goto out;
>>> }

This does not conform to the TCG standard. You MUST return
EFI_BUFFER_TO_SMALL in this case.

>>>
>>> The last field in the 1.0 struct was ManufacturerID, so you should
>>> be able to detect 1.0 clients that expect that based on the size they
>>> pass in.

No, you cannot detect 1.0 clients this way.

>>
>> The patch doesn't show the entire u-boot code there and is a bit
>> misleading.
>> There's another check that precedes the one I am patching, which if I
>> am reading it correctly does exactly what you describe
>>
>> #define BOOT_SERVICE_CAPABILITY_MIN \
>>            offsetof(struct efi_tcg2_boot_service_capability,
>> number_of_pcr_banks)
>> if (capability->size < BOOT_SERVICE_CAPABILITY_MIN) {
>>            capability->size = BOOT_SERVICE_CAPABILITY_MIN;
>>            efi_ret = EFI_BUFFER_TOO_SMALL;
>>            goto out;
>> }
>
> I took a look at the u-boot code.  What I think we want is this:
>
> diff --git a/include/efi_tcg2.h b/include/efi_tcg2.h
> index b1c3abd097..6a46d9e51c 100644
> --- a/include/efi_tcg2.h
> +++ b/include/efi_tcg2.h
> @@ -126,7 +126,7 @@ struct efi_tcg2_boot_service_capability {
>   };
>
>   /* up to and including the vendor ID (manufacturer_id) field */
> -#define BOOT_SERVICE_CAPABILITY_MIN \
> +#define BOOT_SERVICE_CAPABILITY_1_0 \
>          offsetof(struct efi_tcg2_boot_service_capability,
> number_of_pcr_banks)
>
>   #define TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03 "Spec ID Event03"
> diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c
> index a83ae7a46c..d37b896b7e 100644
> --- a/lib/efi_loader/efi_tcg2.c
> +++ b/lib/efi_loader/efi_tcg2.c
> @@ -742,15 +742,15 @@ efi_tcg2_get_capability(struct efi_tcg2_protocol
> *this,
>                  goto out;
>          }
>
> -       if (capability->size < BOOT_SERVICE_CAPABILITY_MIN) {
> -               capability->size = BOOT_SERVICE_CAPABILITY_MIN;
> +       if (capability->size < BOOT_SERVICE_CAPABILITY_1_0) {
> +               capability->size = sizeof(*capability);
>                  efi_ret = EFI_BUFFER_TOO_SMALL;
>                  goto out;
>          }
>
>          if (capability->size < sizeof(*capability)) {
>                  capability->size = sizeof(*capability);
> -               efi_ret = EFI_BUFFER_TOO_SMALL;
> +               efi_ret = EFI_UNSUPPORTED;
>                  goto out;
>          }
>
> That is:
>
> -if the passed in size is less than the 1.0 struct size
>   then return sizeof() the full struct.  This allows
>   client to query and determine the supported struct
>   size
>
> -to detect and reject 1.0 clients, if the passed in
>   size is >= 1.0 struct size AND less than sizeof(*capability)
>   then return EFI_UNSUPPORTED
>
> Does that make sense?

No, this will not work. A 1.0 client may call with size = 0 and next
will call with whatever size was returned accompanied by
EFI_BUFFER_TOO_SMALL.

It is the client's obligation to check the values of fields
StructureVersion and ProtocolVersion.

Best regards

Heinrich

>
> Thanks,
> Stuart



More information about the U-Boot mailing list