audk/SecurityPkg/DeviceSecurity/SpdmSecurityLib/SpdmConnectionInit.c

482 lines
15 KiB
C

/** @file
EDKII Device Security library for SPDM device.
It follows the SPDM Specification.
Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "SpdmSecurityLibInternal.h"
LIST_ENTRY mSpdmDeviceContextList = INITIALIZE_LIST_HEAD_VARIABLE (mSpdmDeviceContextList);
#define CONNECTUIN_FAILURE_GET_SPDM_UID_FAILED "Fail to get Spdm Uid"
#define CONNECTUIN_FAILURE_STGNATURE_DB_FUL_STRING "The Signature database devdb is full"
/**
record Spdm Io protocol into the context list.
@param[in] SpdmDeviceContext The SPDM context of the device.
**/
VOID
RecordSpdmDeviceContextInList (
IN SPDM_DEVICE_CONTEXT *SpdmDeviceContext
)
{
SPDM_DEVICE_CONTEXT_INSTANCE *NewSpdmDeviceContext;
LIST_ENTRY *SpdmDeviceContextList;
SpdmDeviceContextList = &mSpdmDeviceContextList;
NewSpdmDeviceContext = AllocateZeroPool (sizeof (*NewSpdmDeviceContext));
if (NewSpdmDeviceContext == NULL) {
ASSERT (NewSpdmDeviceContext != NULL);
return;
}
NewSpdmDeviceContext->Signature = SPDM_DEVICE_CONTEXT_INSTANCE_SIGNATURE;
NewSpdmDeviceContext->SpdmDeviceContext = SpdmDeviceContext;
InsertTailList (SpdmDeviceContextList, &NewSpdmDeviceContext->Link);
}
/**
get Spdm Io protocol from Context list via spdm context.
@param[in] SpdmContext The SPDM context of the requester.
return a pointer to the Spdm Io protocol.
**/
VOID *
EFIAPI
GetSpdmIoProtocolViaSpdmContext (
IN VOID *SpdmContext
)
{
LIST_ENTRY *Link;
SPDM_DEVICE_CONTEXT_INSTANCE *CurrentSpdmDeviceContext;
LIST_ENTRY *SpdmDeviceContextList;
SpdmDeviceContextList = &mSpdmDeviceContextList;
Link = GetFirstNode (SpdmDeviceContextList);
while (!IsNull (SpdmDeviceContextList, Link)) {
CurrentSpdmDeviceContext = SPDM_DEVICE_CONTEXT_INSTANCE_FROM_LINK (Link);
if (CurrentSpdmDeviceContext->SpdmDeviceContext->SpdmContext == SpdmContext) {
return CurrentSpdmDeviceContext->SpdmDeviceContext->SpdmIoProtocol;
}
Link = GetNextNode (SpdmDeviceContextList, Link);
}
return NULL;
}
/**
creates and returns Spdm Uid from the volatile variable.
@param[in] SpdmUid A pointer to Spdm Uid.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_OUT_OF_RESOURCES Out of memory.
@retval EFI_DEVICE_ERROR The operation was unsuccessful.
**/
EFI_STATUS
GetSpdmUid (
UINT64 *SpdmUid
)
{
EFI_STATUS Status;
UINTN VarSize;
UINT64 Uid;
VarSize = sizeof (*SpdmUid);
Status = gRT->GetVariable (
L"SpdmUid",
&gEfiDeviceSecuritySpdmUidGuid,
NULL,
&VarSize,
&Uid
);
if (Status == EFI_NOT_FOUND) {
Uid = 0;
} else if (EFI_ERROR (Status)) {
return Status;
}
*SpdmUid = Uid++;
Status = gRT->SetVariable (
L"SpdmUid",
&gEfiDeviceSecuritySpdmUidGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (Uid),
&Uid
);
return Status;
}
/**
Record and log the connection failure string to PCR1.
@param[in] FailureString The failure string.
@param[in] StringLen The length of the string.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_OUT_OF_RESOURCES Out of memory.
@retval EFI_DEVICE_ERROR The operation was unsuccessful.
**/
EFI_STATUS
RecordConnectionFailureStatus (
IN CHAR8 *FailureString,
IN UINT32 StringLen
)
{
EFI_STATUS Status;
Status = TpmMeasureAndLogData (
1,
EV_PLATFORM_CONFIG_FLAGS,
FailureString,
StringLen,
FailureString,
StringLen
);
DEBUG ((DEBUG_INFO, "RecordConnectionFailureStatus %r\n", Status));
return Status;
}
/**
This function creates the spdm device context and init connection to the
responder with the device info.
@param[in] SpdmDeviceInfo A pointer to device info.
@param[out] SecurityState A pointer to the security state of the requester.
@return the spdm device conext after the init connection succeeds.
**/
SPDM_DEVICE_CONTEXT *
EFIAPI
CreateSpdmDeviceContext (
IN EDKII_SPDM_DEVICE_INFO *SpdmDeviceInfo,
OUT EDKII_DEVICE_SECURITY_STATE *SecurityState
)
{
SPDM_DEVICE_CONTEXT *SpdmDeviceContext;
VOID *SpdmContext;
UINTN SpdmContextSize;
VOID *ScratchBuffer;
UINTN ScratchBufferSize;
EFI_STATUS Status;
SPDM_RETURN SpdmReturn;
EFI_SIGNATURE_LIST *DbList;
EFI_SIGNATURE_DATA *Cert;
UINTN CertCount;
UINTN Index;
UINTN SiglistHeaderSize;
UINTN DbSize;
VOID *Data;
UINTN DataSize;
SPDM_DATA_PARAMETER Parameter;
UINT8 Data8;
UINT16 Data16;
UINT32 Data32;
UINT8 AuthState;
SpdmDeviceContext = AllocateZeroPool (sizeof (*SpdmDeviceContext));
if (SpdmDeviceContext == NULL) {
ASSERT (SpdmDeviceContext != NULL);
return NULL;
}
SpdmDeviceContext->Signature = SPDM_DEVICE_CONTEXT_SIGNATURE;
CopyMem (&SpdmDeviceContext->DeviceId, SpdmDeviceInfo->DeviceId, sizeof (EDKII_DEVICE_IDENTIFIER));
SpdmDeviceContext->IsEmbeddedDevice = SpdmDeviceInfo->IsEmbeddedDevice;
SpdmContextSize = SpdmGetContextSize ();
SpdmContext = AllocateZeroPool (SpdmContextSize);
if (SpdmContext == NULL) {
ASSERT (SpdmContext != NULL);
goto Error;
}
SpdmReturn = SpdmInitContext (SpdmContext);
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
goto Error;
}
SpdmRegisterDeviceIoFunc (
SpdmContext,
SpdmDeviceInfo->SendMessage,
SpdmDeviceInfo->ReceiveMessage
);
SpdmRegisterTransportLayerFunc (
SpdmContext,
SpdmDeviceInfo->MaxSpdmMsgSize,
SpdmDeviceInfo->TransportHeaderSize,
SpdmDeviceInfo->TransportTailSize,
SpdmDeviceInfo->TransportEncodeMessage,
SpdmDeviceInfo->TransportDecodeMessage
);
SpdmRegisterDeviceBufferFunc (
SpdmContext,
SpdmDeviceInfo->SenderBufferSize,
SpdmDeviceInfo->ReceiverBufferSize,
SpdmDeviceInfo->AcquireSenderBuffer,
SpdmDeviceInfo->ReleaseSenderBuffer,
SpdmDeviceInfo->AcquireReceiverBuffer,
SpdmDeviceInfo->ReleaseReceiverBuffer
);
ScratchBufferSize = SpdmGetSizeofRequiredScratchBuffer (SpdmContext);
ScratchBuffer = AllocateZeroPool (ScratchBufferSize);
if (ScratchBuffer == NULL) {
ASSERT (ScratchBuffer != NULL);
goto Error;
}
SpdmSetScratchBuffer (SpdmContext, ScratchBuffer, ScratchBufferSize);
SpdmDeviceContext->SpdmContextSize = SpdmContextSize;
SpdmDeviceContext->SpdmContext = SpdmContext;
SpdmDeviceContext->ScratchBufferSize = ScratchBufferSize;
SpdmDeviceContext->ScratchBuffer = ScratchBuffer;
Status = gBS->HandleProtocol (
SpdmDeviceContext->DeviceId.DeviceHandle,
&gEfiDevicePathProtocolGuid,
(VOID **)&SpdmDeviceContext->DevicePath
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Locate - DevicePath - %r\n", Status));
goto Error;
}
Status = gBS->HandleProtocol (
SpdmDeviceContext->DeviceId.DeviceHandle,
&SpdmDeviceContext->DeviceId.DeviceType,
(VOID **)&SpdmDeviceContext->DeviceIo
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Locate - DeviceIo - %r\n", Status));
// This is optional, only check known device type later.
}
if (SpdmDeviceInfo->SpdmIoProtocolGuid != NULL) {
Status = gBS->HandleProtocol (
SpdmDeviceContext->DeviceId.DeviceHandle,
SpdmDeviceInfo->SpdmIoProtocolGuid,
(VOID **)&SpdmDeviceContext->SpdmIoProtocol
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Locate - SpdmIoProtocol - %r\n", Status));
goto Error;
}
}
if (CompareGuid (&SpdmDeviceContext->DeviceId.DeviceType, &gEdkiiDeviceIdentifierTypePciGuid)) {
if (SpdmDeviceContext->DeviceIo == NULL) {
DEBUG ((DEBUG_ERROR, "Locate - PciIo - %r\n", Status));
goto Error;
}
}
Status = GetSpdmUid (&SpdmDeviceContext->DeviceUID);
if (EFI_ERROR (Status)) {
Status = RecordConnectionFailureStatus (
CONNECTUIN_FAILURE_GET_SPDM_UID_FAILED,
sizeof (CONNECTUIN_FAILURE_GET_SPDM_UID_FAILED)
);
if (EFI_ERROR (Status)) {
goto Error;
}
ASSERT (FALSE);
DEBUG ((DEBUG_ERROR, "Fail to get UID - %r\n", Status));
goto Error;
}
RecordSpdmDeviceContextInList (SpdmDeviceContext);
Status = GetVariable2 (
EFI_DEVICE_SECURITY_DATABASE,
&gEfiDeviceSignatureDatabaseGuid,
(VOID **)&SpdmDeviceContext->SignatureList,
&SpdmDeviceContext->SignatureListSize
);
if ((!EFI_ERROR (Status)) && (SpdmDeviceContext->SignatureList != NULL)) {
DbList = SpdmDeviceContext->SignatureList;
DbSize = SpdmDeviceContext->SignatureListSize;
while ((DbSize > 0) && (SpdmDeviceContext->SignatureListSize >= DbList->SignatureListSize)) {
if (DbList->SignatureListSize == 0) {
break;
}
if ( (!CompareGuid (&DbList->SignatureType, &gEfiCertX509Guid))
|| (DbList->SignatureHeaderSize != 0)
|| (DbList->SignatureSize < sizeof (EFI_SIGNATURE_DATA)))
{
DbSize -= DbList->SignatureListSize;
DbList = (EFI_SIGNATURE_LIST *)((UINT8 *)DbList + DbList->SignatureListSize);
continue;
}
SiglistHeaderSize = sizeof (EFI_SIGNATURE_LIST) + DbList->SignatureHeaderSize;
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)DbList + SiglistHeaderSize);
CertCount = (DbList->SignatureListSize - SiglistHeaderSize) / DbList->SignatureSize;
for (Index = 0; Index < CertCount; Index++) {
Data = Cert->SignatureData;
DataSize = DbList->SignatureSize - sizeof (EFI_GUID);
ZeroMem (&Parameter, sizeof (Parameter));
Parameter.location = SpdmDataLocationLocal;
SpdmReturn = SpdmSetData (SpdmContext, SpdmDataPeerPublicRootCert, &Parameter, Data, DataSize);
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
if (SpdmReturn == LIBSPDM_STATUS_BUFFER_FULL) {
Status = RecordConnectionFailureStatus (
CONNECTUIN_FAILURE_STGNATURE_DB_FUL_STRING,
sizeof (CONNECTUIN_FAILURE_STGNATURE_DB_FUL_STRING)
);
if (EFI_ERROR (Status)) {
goto Error;
}
ASSERT (FALSE);
}
goto Error;
}
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + DbList->SignatureSize);
}
DbSize -= DbList->SignatureListSize;
DbList = (EFI_SIGNATURE_LIST *)((UINT8 *)DbList + DbList->SignatureListSize);
}
}
Data8 = 0;
ZeroMem (&Parameter, sizeof (Parameter));
Parameter.location = SpdmDataLocationLocal;
SpdmReturn = SpdmSetData (SpdmContext, SpdmDataCapabilityCTExponent, &Parameter, &Data8, sizeof (Data8));
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
ASSERT (FALSE);
goto Error;
}
Data32 = 0;
SpdmReturn = SpdmSetData (SpdmContext, SpdmDataCapabilityFlags, &Parameter, &Data32, sizeof (Data32));
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
ASSERT (FALSE);
goto Error;
}
Data8 = SPDM_MEASUREMENT_SPECIFICATION_DMTF;
SpdmReturn = SpdmSetData (SpdmContext, SpdmDataMeasurementSpec, &Parameter, &Data8, sizeof (Data8));
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
ASSERT (FALSE);
goto Error;
}
if (SpdmDeviceInfo->BaseAsymAlgo != 0) {
Data32 = SpdmDeviceInfo->BaseAsymAlgo;
} else {
Data32 = SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSASSA_2048 |
SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSASSA_3072 |
SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSASSA_4096 |
SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_ECDSA_ECC_NIST_P256 |
SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_ECDSA_ECC_NIST_P384 |
SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_ECDSA_ECC_NIST_P521;
}
SpdmReturn = SpdmSetData (SpdmContext, SpdmDataBaseAsymAlgo, &Parameter, &Data32, sizeof (Data32));
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
ASSERT (FALSE);
goto Error;
}
if (SpdmDeviceInfo->BaseHashAlgo != 0) {
Data32 = SpdmDeviceInfo->BaseHashAlgo;
} else {
Data32 = SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_256 |
SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_384 |
SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_512;
}
SpdmReturn = SpdmSetData (SpdmContext, SpdmDataBaseHashAlgo, &Parameter, &Data32, sizeof (Data32));
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
ASSERT (FALSE);
goto Error;
}
SpdmReturn = SpdmInitConnection (SpdmContext, FALSE);
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
DEBUG ((DEBUG_ERROR, "SpdmInitConnection - %p\n", SpdmReturn));
AuthState = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_NO_SPDM;
SecurityState->AuthenticationState = EDKII_DEVICE_SECURITY_STATE_ERROR_DEVICE_NO_CAPABILITIES;
Status = ExtendCertificate (SpdmDeviceContext, AuthState, 0, NULL, NULL, 0, 0, SecurityState);
if (Status != EFI_SUCCESS) {
DEBUG ((DEBUG_ERROR, "ExtendCertificate AUTH_STATE_NO_SPDM failed\n"));
}
goto Error;
}
ZeroMem (&Parameter, sizeof (Parameter));
Parameter.location = SpdmDataLocationConnection;
DataSize = sizeof (Data16);
SpdmReturn = SpdmGetData (SpdmContext, SpdmDataSpdmVersion, &Parameter, &Data16, &DataSize);
if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
DEBUG ((DEBUG_ERROR, "SpdmGetData - %p\n", SpdmReturn));
goto Error;
}
SpdmDeviceContext->SpdmVersion = (Data16 >> SPDM_VERSION_NUMBER_SHIFT_BIT);
return SpdmDeviceContext;
Error:
DestroySpdmDeviceContext (SpdmDeviceContext);
return NULL;
}
/**
This function destories the spdm device context.
@param[in] SpdmDeviceContext A pointer to device info.
**/
VOID
EFIAPI
DestroySpdmDeviceContext (
IN SPDM_DEVICE_CONTEXT *SpdmDeviceContext
)
{
// need zero memory in case of secret in memory.
if (SpdmDeviceContext->SpdmContext != NULL) {
ZeroMem (SpdmDeviceContext->SpdmContext, SpdmDeviceContext->SpdmContextSize);
FreePool (SpdmDeviceContext->SpdmContext);
}
if (SpdmDeviceContext->ScratchBuffer != NULL) {
ZeroMem (SpdmDeviceContext->ScratchBuffer, SpdmDeviceContext->ScratchBufferSize);
FreePool (SpdmDeviceContext->ScratchBuffer);
}
if (SpdmDeviceContext->SignatureList != NULL) {
ZeroMem (SpdmDeviceContext->SignatureList, SpdmDeviceContext->SignatureListSize);
FreePool (SpdmDeviceContext->SignatureList);
}
FreePool (SpdmDeviceContext);
}