// // This file contains a 'Sample Driver' and is licensed as such // under the terms of your license agreement with Intel or your // vendor. This file may be modified by the user, subject to // the additional terms of the license agreement // /** Manage & verify Timebased Authenticated varaibles in KeyEnroll tool. Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.
This software and associated documentation (if any) is furnished under a license and may only be used or copied in accordance with the terms of the license. Except as permitted by such license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. **/ #include "AuthVariable.h" extern LIST_ENTRY mVarListEntry; extern EFI_GUID gEfiCertTypePkcs7Guid; extern UINT8 mHashOidValueSha256; /* Function will split the "Buffer" parameter and getting the Auth2 descriptor, and doing verification. @param[in] Attributes Attribute value of the variable. @param[in] Buffer Data pointer. @param[in] BufferSize The size of Data in bytes. @param[in] OrgTimeStamp Pointer to original time stamp, original variable is not found if NULL. @retval EFI_SECURITY_VIOLATION Verification fail. @retval EFI_SUCCESS Operation success. */ EFI_STATUS VerifyAuth2Descriptor ( IN UINT32 Attributes, IN VOID *Buffer, IN UINTN BufferSize, IN EFI_TIME *OrgTimeStamp, OUT UINT8 **CertDataPtr, OUT UINT32 *CertDataSize ) { EFI_VARIABLE_AUTHENTICATION_2 *Auth2Ptr; EFI_TIME *Auth2TimeStamp; if (Buffer == NULL || CertDataPtr == NULL || CertDataSize == NULL) { return EFI_INVALID_PARAMETER; } Auth2Ptr = (EFI_VARIABLE_AUTHENTICATION_2 *)Buffer; Auth2TimeStamp = &Auth2Ptr->TimeStamp; if ( (Auth2TimeStamp->Pad1 != 0) || (Auth2TimeStamp->Pad2 != 0) || (Auth2TimeStamp->Nanosecond != 0) || (Auth2TimeStamp->TimeZone != 0) || (Auth2TimeStamp->Daylight !=0) ) { return EFI_SECURITY_VIOLATION; } if ((OrgTimeStamp != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) { // // TimeStamp check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION. // if (CompareTimeStamp (Auth2TimeStamp, OrgTimeStamp)) { return EFI_SECURITY_VIOLATION; } } if ( (Auth2Ptr->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) || (CompareGuid (&Auth2Ptr->AuthInfo.CertType, &gEfiCertTypePkcs7Guid)) ) { // // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION. // return EFI_SECURITY_VIOLATION; } *CertDataPtr = ((UINT8 *)&Auth2Ptr->AuthInfo.CertType) + sizeof (EFI_GUID); *CertDataSize = Auth2Ptr->AuthInfo.Hdr.dwLength - (UINT32)(OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertType)) - sizeof (EFI_GUID); if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) { if (*CertDataSize >= (13 + sizeof (mHashOidValueSha256))) { if ( ((*(((UINT8 *)&Auth2Ptr->AuthInfo.CertType) + sizeof (EFI_GUID) + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) || (CompareMem (*CertDataPtr + 13, &mHashOidValueSha256, sizeof (mHashOidValueSha256)) != 0) ) { return EFI_SECURITY_VIOLATION; } } } return EFI_SUCCESS; } /* This function will according to the opeartion type to processing the certs of auth variable. @param[in] VariableName Name of Variable to be found. @param[in] VendorGuid Variable vendor GUID. @param[in] Attributes Attribute value of the variable. @param[in] OperationType Indicate the operation of auth variable. @param[in] PayloadSize Indicate the size of payload in bytes. @param[in] SignerCert A pointer to SignerCert data. @param[in] SignerCertSize Length of SignerCert data. @param[in] TopLevelCert A pointer to TopLevelCert data. @param[in] TopLevelCertSize Length of TopLevelCert data. @retval EFI_INVALID_PARAMETER Invalid input parameters. @retval EFI_SECURITY_VIOLATION Verify or update certs failed. @retval EFI_SUCCESS The operation is finished successfully. @retval Others Other errors as indicated. */ EFI_STATUS VerifyCertsAndUpdate ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN EFI_COMMAND_OPER_TYPE OperationType, IN UINT32 PayloadSize, IN UINT8 *SignerCert, IN UINT32 SignerCertSize, IN UINT8 *TopLevelCert, IN UINT32 TopLevelCertSize ) { EFI_STATUS Status; EFI_CERT_DATA *TempPtr; UINT8 *CertDataPtr; UINT32 CertDataSize; UINT8 Sha256Digest[SHA256_DIGEST_SIZE]; VARIABLE_INFO_PRIVATE *PriVarInfo; Status = EFI_SUCCESS; TempPtr = NULL; CertDataPtr = NULL; CertDataSize = 0; if ( OperationType >= VariableTypeMax || VariableName == NULL || VendorGuid == NULL || SignerCert == NULL || TopLevelCert == NULL ) { return EFI_INVALID_PARAMETER; } PriVarInfo = FindVariableInfoPtr (VariableName, VendorGuid); /* Add: 1. Create: certdb, variable. OrgTimeStamp == NULL, payloadsize != 0. 2. Modify: variable. OrgTimeStamp != NULL, payloadsize != 0. Del: 1. Delete: certdb, variable. payloadsize == 0. Append:(No update timestamp) 1. Create: certdb, variable. OrgTimeStamp == NULL, payloadsize != 0. 2. Modify: variable. OrgTimeStamp != NULL, payloadsize != 0. */ // // Specified behavior is not matching the payload buffer. // In this situation will delete certs from certdb but add variable. // if (OperationType == VariableTypeDel && PayloadSize != 0) { Error(NULL, 0, 0, "Operation type is not matching the .bin file.", NULL); return EFI_SECURITY_VIOLATION; } // // Specified behavior is not matching the payload buffer. // In this situation will add certs into certdb but delete variable. // if ( (OperationType == VariableTypeAdd || OperationType == VariableTypeAppend) && (PayloadSize == 0) ) { Error(NULL, 0, 0, "Operation type is not matching the .bin file.", NULL); return EFI_SECURITY_VIOLATION; } if ((OperationType == VariableTypeAdd || OperationType == VariableTypeAppend) && (PriVarInfo == NULL)) { TempPtr = (EFI_CERT_DATA *)(SignerCert + 1); Status = InsertCertsToDb ( VariableName, VendorGuid, Attributes, TempPtr->CertDataBuffer, ReadUnaligned32 ((UINT32 *)&TempPtr->CertDataLength), TopLevelCert, TopLevelCertSize ); if (EFI_ERROR (Status)) { return Status; } } else { Status = FindCertsFromDb ( VariableName, VendorGuid, Attributes, NULL, NULL, &CertDataPtr, &CertDataSize ); if (EFI_ERROR (Status)) { return Status; } if (CertDataSize == SHA256_DIGEST_SIZE) { TempPtr = (EFI_CERT_DATA *)(SignerCert + 1); Status = CalculatePrivAuthVarSignChainSHA256Digest ( TempPtr->CertDataBuffer, ReadUnaligned32 ((UINT32 *)&TempPtr->CertDataLength), TopLevelCert, TopLevelCertSize, Sha256Digest ); if (EFI_ERROR (Status)) { return Status; } if (CompareMem (Sha256Digest, CertDataPtr, CertDataSize) != 0) { return EFI_SECURITY_VIOLATION; } } else { if (CertDataSize != TopLevelCertSize) { return EFI_SECURITY_VIOLATION; } if (CompareMem (SignerCert, CertDataPtr, CertDataSize) != 0) { return EFI_SECURITY_VIOLATION; } } } return EFI_SUCCESS; } /** Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set @param[in] VariableName Name of Variable to be found. @param[in] VendorGuid Variable vendor GUID. @param[in] Attributes Attribute value of the variable. @param[in] Buffer Data pointer. @param[in] BufferSize Size of Data found. If size is less than the data, this value contains the required size. @param[in] OrgTimeStamp Pointer to original time stamp, original variable is not found if NULL. @param[in] OperationType Indicates how to process the auth variable. @param[out] VarPayloadPtr Pointer to variable payload address. @param[out] VarPayloadSize Pointer to variable payload size. @param[out] CertTimeStamp Pointer to timestamp of auth2 structure. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation check carried out by the firmware. @retval EFI_OUT_OF_RESOURCES Failed to process variable due to lack of resources. @retval EFI_SUCCESS Variable pass validation successfully. **/ EFI_STATUS Auth2VerifyAndUpdate ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN VOID *Buffer, IN UINTN BufferSize, IN EFI_TIME *OrgTimeStamp, IN EFI_COMMAND_OPER_TYPE OperationType, OUT UINT8 **VarPayloadPtr, OUT UINTN *VarPayloadSize, OUT EFI_TIME **CertTimeStamp ) { EFI_STATUS Status; BOOLEAN VerifyStatus; EFI_VARIABLE_AUTHENTICATION_2 *Auth2Ptr; UINT8 *CertDataPtr; UINT32 CertDataSize; UINT8 *PayloadPtr; UINT32 PayloadSize; UINT8 *Digest; UINT32 DigestSize; UINT8 *SignerCert; UINT32 SignerCertSize; UINT8 *TopLevelCert; UINT32 TopLevelCertSize; UINT32 Offset; VARIABLE_INFO_PRIVATE *PriVarInfo; Status = EFI_SUCCESS; Digest = NULL; Offset = 0; PriVarInfo = FindVariableInfoPtr (VariableName, VendorGuid); Status = VerifyAuth2Descriptor ( Attributes, Buffer, BufferSize, PriVarInfo == NULL ? NULL : &PriVarInfo->TimeStamp, &CertDataPtr, &CertDataSize ); if (EFI_ERROR (Status)) { return Status; } Auth2Ptr = (EFI_VARIABLE_AUTHENTICATION_2 *)Buffer; // // Find out the new data payload which follows Pkcs7 SignedData directly. // PayloadPtr = CertDataPtr + CertDataSize; PayloadSize = BufferSize - sizeof (EFI_TIME) - Auth2Ptr->AuthInfo.Hdr.dwLength; DigestSize = PayloadSize + KeyEnrollStrLen (VariableName) * sizeof (CHAR16) + sizeof (UINT32) + sizeof (EFI_TIME) + sizeof (EFI_GUID); Digest = (UINT8 *)malloc (DigestSize); if (Digest == NULL) { return EFI_OUT_OF_RESOURCES; } CopyMem (Digest + Offset, VariableName, KeyEnrollStrLen (VariableName) * sizeof (CHAR16)); Offset = Offset + KeyEnrollStrLen (VariableName) * sizeof (CHAR16); CopyMem (Digest + Offset, VendorGuid, sizeof (EFI_GUID)); Offset = Offset + sizeof (EFI_GUID); CopyMem (Digest + Offset, &Attributes, sizeof (UINT32)); Offset = Offset + sizeof (UINT32); CopyMem (Digest + Offset, &Auth2Ptr->TimeStamp, sizeof (EFI_TIME)); Offset = Offset + sizeof (EFI_TIME); CopyMem (Digest + Offset, PayloadPtr, PayloadSize); VerifyStatus = Pkcs7GetSigners ( CertDataPtr, CertDataSize, &SignerCert, (UINTN *)&SignerCertSize, &TopLevelCert, (UINTN *)&TopLevelCertSize ); if (!VerifyStatus) { Error (NULL, 0, 0, "Pkcs7GetSigners Fail.", NULL); goto ON_EXIT; } VerifyStatus = Pkcs7Verify ( CertDataPtr, CertDataSize, TopLevelCert, TopLevelCertSize, Digest, DigestSize ); if (!VerifyStatus) { Error (NULL, 0, 0, "Pkcs7Verify Fail.", NULL); goto ON_EXIT; } Status = VerifyCertsAndUpdate ( VariableName, VendorGuid, Attributes, OperationType, PayloadSize, SignerCert, SignerCertSize, TopLevelCert, TopLevelCertSize ); if (EFI_ERROR (Status)) { VerifyStatus = FALSE; } ON_EXIT: Pkcs7FreeSigners (TopLevelCert); Pkcs7FreeSigners (SignerCert); FREE_NON_NULL_PTR (Digest); if (!VerifyStatus) { return EFI_SECURITY_VIOLATION; } // // Find out the new data payload which follows Pkcs7 SignedData directly. // *VarPayloadPtr = CertDataPtr + CertDataSize; *VarPayloadSize = BufferSize - sizeof (EFI_TIME) - Auth2Ptr->AuthInfo.Hdr.dwLength; *CertTimeStamp = &Auth2Ptr->TimeStamp; return EFI_SUCCESS; } /* Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set. @param[in] Name Name of Variable to be processed. @param[in] Guid Variable vendor GUID. @param[in] Data Data pointer. @param[in] DataSize The size of Data in bytes. @param[in] Attributes Attribute value of the variable. @param[in] OperType The operation type about variable. @retval EFI_SUCCESS The operation is finished successfully. @retval EFI_INVALID_PARAMETER Invalid input parameters, Name, Guid or Data is NULL. @retval EFI_OUT_OF_RESOURCES No more memory for allocation. @retval Others Other errors as indicated. */ EFI_STATUS ProcessAuthVar ( IN CHAR8 *Name, IN EFI_GUID *Guid, IN VOID *Data, IN UINTN DataSize, IN UINT32 Attributes, IN EFI_COMMAND_OPER_TYPE OperType ) { EFI_STATUS Status; EFI_TIME *CertTimeStamp; LIST_ENTRY *ListEntry; VARIABLE_INFO_PRIVATE *PriVarInfo; CHAR16 *UnicodeVariableName; UINT8 *PayloadPtr; UINTN PayloadSize; Status = EFI_SUCCESS; CertTimeStamp = NULL; ListEntry = NULL; PriVarInfo = NULL; UnicodeVariableName = NULL; PayloadPtr = NULL; PayloadSize = 0; if (Name == NULL || Guid == NULL || Data == NULL || OperType == VariableTypeMax) { return EFI_INVALID_PARAMETER; } UnicodeVariableName = malloc ((strlen (Name) + 1) * sizeof (CHAR16)); if (UnicodeVariableName == NULL) { return EFI_OUT_OF_RESOURCES; } Ascii2UnicodeString (Name, UnicodeVariableName); // // Try to get auth variable. // ListEntry = FindVariableList (&mVarListEntry, UnicodeVariableName, Guid); if (ListEntry != NULL) { PriVarInfo = VARIABLE_INFO_PRIVATE_FROM_LINK (ListEntry); } Status = Auth2VerifyAndUpdate ( UnicodeVariableName, Guid, Attributes, Data, DataSize, PriVarInfo == NULL ? NULL : &PriVarInfo->TimeStamp, OperType, &PayloadPtr, &PayloadSize, &CertTimeStamp ); if (EFI_ERROR (Status)) { return Status; } if (OperType == VariableTypeDel) { Status = DeleteVariableList (&mVarListEntry, UnicodeVariableName, Guid); if (Status == EFI_SUCCESS) { Status = DeleteCertsFromDb (UnicodeVariableName, Guid, Attributes); } } else { Status = CreateVariableList ( &mVarListEntry, UnicodeVariableName, Guid, Attributes, CertTimeStamp, PayloadSize, PayloadPtr, OperType == VariableTypeAppend ); } FREE_NON_NULL_PTR (UnicodeVariableName); return Status; }