alder_lake_bios/Insyde/InsydeNetworkPkg/Drivers/TlsDxe/TlsAuthConfig.c

2836 lines
88 KiB
C

/** @file
TLS certificate management related functions.
;******************************************************************************
;* Copyright (c) 2019 - 2020, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "TlsAuthConfig.h"
#include "TlsAuthConfigNvData.h"
#include <Library/UefiBootManagerLib.h>
#include <Library/DevicePathLib.h>
#include <Guid/MdeModuleHii.h>
STATIC CHAR16 mTlcAuthManageStorageName[] = L"TLS_AUTH_CERT_MANAGE_IFR_NVDATA";
STATIC CRYPTO_SERVICES_PROTOCOL *mCryptoServices;
STATIC BOOLEAN mgH2OFormBrowserProtocoSupported = FALSE;
STATIC ENROLL_MENU_OPTION mFileListFsOptionMenu = {
EM_MENU_ENTRY_SIGNATURE,
NULL,
0
};
STATIC ENROLL_MENU_OPTION mFileListDirectoryMenu = {
EM_MENU_ENTRY_SIGNATURE,
NULL,
0
};
STATIC ENROLL_MENU_OPTION mFileListCertFileMenu = {
EM_MENU_ENTRY_SIGNATURE,
NULL,
0
};
STATIC EFI_TEXT_STRING mOriginalOutputString;
STATIC STRING_DEPOSITORY *mSecureBootFileOptionStrDepository;
STATIC
EFI_STATUS
ProcessFile (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN ENROLL_MENU_ENTRY *MenuEntry,
IN EM_FILE_CONTEXT *FileContext
);
STATIC
EFI_STATUS
ProcessCertificatesFromCertStore (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN UINT16 SelectIndex,
IN CHAR16 *OutputFileName
);
STATIC
BOOLEAN
IsGuidExist (
IN EFI_GUID *TargetCertGuid
);
/**
Reclaim string depository by moving the current node pointer to list head.
@param[in] StringDepository Pointer to the string repository
**/
VOID
FileListReclaimStrDepository (
IN STRING_DEPOSITORY *StrDepository
)
{
if (StrDepository != NULL) {
StrDepository->CurrentNode = StrDepository->ListHead;
}
}
/**
Translate PEM to DER.
@param[in] Data Pointer to X509 data, it's possible PEM or DER content.
@param[in] DataSize size of X509 data.
@param[out] DerData Pointer to data pointer to output translated DER data.
@param[out] DerDataSize Pointer the size pointer to output DER data size.
@return String token or zero if input parameter is NULL or fail to allocate memory.
**/
STATIC
EFI_STATUS
TranslatePemToDer (
IN UINT8 *Data,
IN UINTN DataSize,
OUT UINT8 **DerData,
OUT UINTN *DerSize
)
{
BOOLEAN BooleanStatus;
UINT8 *TmpDerData;
UINTN TmpDerSize;
if (mCryptoServices == NULL) {
return EFI_UNSUPPORTED;
}
TmpDerData = NULL;
TmpDerSize = 0;
BooleanStatus = mCryptoServices->X509GetCaCertificateFromPemToDer (Data, DataSize, &TmpDerData, &TmpDerSize);
if (!BooleanStatus) {
return EFI_NOT_FOUND;
}
*DerData = TmpDerData;
*DerSize = TmpDerSize;
return EFI_SUCCESS;
}
/**
Fetch a usable string node from the string depository and return the string token.
@param[in] CallbackData Pointer to SECURE_BOOT_MANAGER_CALLBACK_DATA instance
@param[in] StringDepository Pointer of the string depository
@return String token or zero if input parameter is NULL or fail to allocate memory.
**/
STATIC
EFI_STRING_ID
FileListGetStrTokenFromDepository (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN STRING_DEPOSITORY *StringDepository
)
{
STRING_LIST_NODE *CurrentListNode;
STRING_LIST_NODE *NextListNode;
if (CallbackData == NULL || StringDepository == NULL) {
return 0;
}
CurrentListNode = StringDepository->CurrentNode;
if ((NULL != CurrentListNode) && (NULL != CurrentListNode->Next)) {
//
// Fetch one reclaimed node from the list.
//
NextListNode = StringDepository->CurrentNode->Next;
} else {
//
// If there is no usable node in the list, update the list.
//
NextListNode = AllocateZeroPool (sizeof (STRING_LIST_NODE));
if (NextListNode == NULL) {
return 0;
}
NextListNode->StringToken = HiiSetString (
CallbackData->RegisteredHandle,
NextListNode->StringToken,
L" ",
NULL
);
ASSERT (NextListNode->StringToken != 0);
StringDepository->TotalNodeNumber++;
if (NULL == CurrentListNode) {
StringDepository->ListHead = NextListNode;
} else {
CurrentListNode->Next = NextListNode;
}
}
StringDepository->CurrentNode = NextListNode;
return StringDepository->CurrentNode->StringToken;
}
/**
Wrap original FreePool gBS call in order to decrease code length.
**/
STATIC
VOID
SafeFreePool (
IN VOID **Buffer
)
{
if (Buffer != NULL && *Buffer != NULL) {
gBS->FreePool (*Buffer);
*Buffer = NULL;
}
}
/**
Destroy the menu entry passed in
@param MenuEntry The menu entry need to be destroyed
**/
STATIC
VOID
DestroyMenuEntry (
ENROLL_MENU_ENTRY *MenuEntry
)
{
EM_FILE_CONTEXT *FileContext;
FileContext = (EM_FILE_CONTEXT *) MenuEntry->VariableContext;
if (!FileContext->IsRoot) {
SafeFreePool ((VOID **) &FileContext->DevicePath);
} else {
if (FileContext->FHandle != NULL) {
FileContext->FHandle->Close (FileContext->FHandle);
}
}
SafeFreePool ((VOID **) &FileContext->FileName);
SafeFreePool ((VOID **) &FileContext->Info);
SafeFreePool ((VOID **) &FileContext);
SafeFreePool ((VOID **) &MenuEntry->DisplayString);
SafeFreePool ((VOID **) &MenuEntry->HelpString);
SafeFreePool ((VOID **) &MenuEntry);
}
/**
Dummy function uses to hook original OutputString ().
@param[in] This The protocol instance pointer.
@param[in] String The NULL-terminated string to be displayed on the output
device(s). All output devices must also support the Unicode
drawing character codes defined in this file.
@retval EFI_SUCCESS Always return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
DummyOutputString (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
)
{
return EFI_SUCCESS;
}
/**
Fuction to hook original Outputstring function to disable all of
output strings.
@retval EFI_SUCCESS Always return EFI_SUCCESS.
**/
STATIC
EFI_STATUS
HookOutputString (
VOID
)
{
mOriginalOutputString = gST->ConOut->OutputString;
gST->ConOut->OutputString = DummyOutputString;
return EFI_SUCCESS;
}
/**
Fuction to restore original Outputstring function to ConOut in
system table.
@retval EFI_SUCCESS Always return EFI_SUCCESS.
**/
STATIC
EFI_STATUS
RestoreOutputString (
VOID
)
{
gST->ConOut->OutputString = mOriginalOutputString;
return EFI_SUCCESS;
}
/**
Generate GUID, Calculate hash of Subject and Issuer.
Use this hash as GUID value.
@param[in] Subject Input Subject binary.
@param[in] SubjectSize Input size of Subject binary.
@param[in] Issuer Input Issuer binary.
@param[in] IssuerSize Input size of Issuer binary.
@param[out] Guid Generated GUID value.
**/
STATIC
EFI_STATUS
GenerateGuidFromX509 (
IN UINT16 *Subject,
IN UINTN SubjectSize,
IN UINT16 *Issuer,
IN UINTN IssuerSize,
OUT EFI_GUID *Guid
)
{
UINT8 *Context;
UINTN ContextSize;
BOOLEAN HashStatus;
UINT8 HashOutput[20];
if (mCryptoServices == NULL) {
return EFI_NOT_READY;
}
if ((Subject == NULL) || (Issuer == NULL) || (Guid == NULL)) {
return EFI_INVALID_PARAMETER;
}
ContextSize = mCryptoServices->Sha1GetContextSize ();
Context = AllocateZeroPool (ContextSize);
if (Context == NULL) {
return EFI_OUT_OF_RESOURCES;
}
HashStatus = mCryptoServices->Sha1Init (Context);
if (!HashStatus) {
goto ON_EXIT;
}
HashStatus = mCryptoServices->Sha1Update (Context, Subject, SubjectSize);
if (!HashStatus) {
goto ON_EXIT;
}
HashStatus = mCryptoServices->Sha1Update (Context, Issuer, IssuerSize);
if (!HashStatus) {
goto ON_EXIT;
}
HashStatus = mCryptoServices->Sha1Final (Context, HashOutput);
if (!HashStatus) {
goto ON_EXIT;
}
CopyMem (Guid, HashOutput, sizeof (EFI_GUID));
ON_EXIT:
if (Context != NULL) {
FreePool (Context);
}
return (!HashStatus) ? EFI_NOT_READY : EFI_SUCCESS;
}
/**
CA parse and dump data function, modified from TlsChkCaCertificate function.
@param[in] Data Pointer to the data buffer of a DER-encoded binary
X.509 certificate or PEM-encoded X.509 certificate.
@param[in] DataSize The size of data buffer in bytes.
@retval EFI_SUCCESS The operation succeeded.
@retval EFI_INVALID_PARAMETER The parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Required resources could not be allocated.
@retval EFI_ABORTED Invalid X.509 certificate.
@retval others Error status from external functions.
**/
STATIC
EFI_STATUS
DumpCaCertificate (
IN VOID *Data,
IN UINTN DataSize,
OUT CHAR16 **SubjectName,
OUT CHAR16 **IssuerName,
OUT EFI_GUID *CertGuid
)
{
EFI_STATUS Status;
BOOLEAN BooleanStatus;
UINTN SubjectSize;
UINTN IssuerSize;
UINT8 *CertData;
UINTN CertSize;
UINT8 *DerData;
UINTN DerSize;
CHAR16 *SubjectNameStr;
CHAR16 *IssuerNameStr;
if (mCryptoServices == NULL) {
return EFI_NOT_READY;
}
if ((Data == NULL) || (DataSize == 0)
|| (SubjectName == NULL) || (IssuerName == NULL)) {
return EFI_INVALID_PARAMETER;
}
DerSize = 0;
DerData = NULL;
CertData = Data;
CertSize = DataSize;
Status = TranslatePemToDer (Data, DataSize, &DerData, &DerSize);
if (!EFI_ERROR (Status)) {
CertData = DerData;
CertSize = DerSize;
}
SubjectSize = 0;
IssuerSize = 0;
SubjectNameStr = NULL;
IssuerNameStr = NULL;
BooleanStatus = mCryptoServices->X509GetSubjectNameEx (CertData, CertSize, &SubjectNameStr, &SubjectSize);
if (!BooleanStatus) {
/// SubjectName get failed.
Status = EFI_ABORTED;
goto ON_EXIT;
}
BooleanStatus = mCryptoServices->X509GetIssuerNameEx (CertData, CertSize, &IssuerNameStr, &IssuerSize);
if (!BooleanStatus) {
/// IssuerName get failed.
Status = EFI_ABORTED;
goto ON_EXIT;
}
//
// Use SubjectName and IssuerName to generate GUID.
//
Status = GenerateGuidFromX509 (SubjectNameStr, SubjectSize, IssuerNameStr, IssuerSize, CertGuid);
if (EFI_ERROR (Status)) {
goto ON_EXIT;
}
*SubjectName = SubjectNameStr;
*IssuerName = IssuerNameStr;
if (DerData != NULL) {
FreePool (DerData);
}
return EFI_SUCCESS;
ON_EXIT:
if (SubjectNameStr != NULL) {
FreePool (SubjectNameStr);
}
if (IssuerNameStr != NULL) {
FreePool (IssuerNameStr);
}
if (DerData != NULL) {
FreePool (DerData);
}
return Status;
}
/**
Decode X509 certificate to a string content SubjectName and IssuerName.
@param[in] X509Data Pointer to the data buffer of a DER-encoded binary
X.509 certificate or PEM-encoded X.509 certificate.
@param[in] X509DataSize Size of X509 binary data.
@param[out] X509DataInfo String content SubjectName and IssuerName.
@param[out] Guid GUID generated by SubjectName and IssuerName.
@retval EFI_SUCCESS The operation succeeded.
@retval EFI_INVALID_PARAMETER The parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Required resources could not be allocated.
@retval EFI_NOT_READY CryptoServices protocol is not ready.
@retval others Error status from external functions.
**/
STATIC
EFI_STATUS
DecodeX509ToSubjectAndIssuerName (
IN VOID *X509Data,
IN UINTN X509DataSize,
OUT CHAR16 **X509DataInfo,
OUT EFI_GUID *Guid
)
{
EFI_STATUS Status;
CHAR16 *SubjectName;
CHAR16 *IssuerName;
CHAR16 *OutputStringBuf;
UINTN OutputStringMaxSize;
if ((X509Data == NULL) || (X509DataInfo == NULL) || (Guid == NULL)) {
return EFI_INVALID_PARAMETER;
}
if (mCryptoServices == NULL) {
return EFI_NOT_READY;
}
SubjectName = NULL;
IssuerName = NULL;
Status = DumpCaCertificate (X509Data, X509DataSize, &SubjectName, &IssuerName, Guid);
if (EFI_ERROR (Status)) {
return Status;
}
OutputStringMaxSize = sizeof (CHAR16) * TLS_X509_INFO_STRING_MAX_SIZE;
OutputStringBuf = AllocateZeroPool (OutputStringMaxSize);
if (OutputStringBuf == NULL) {
goto ON_ERROR;
}
UnicodeSPrint (OutputStringBuf, OutputStringMaxSize, L"SubjectName:%s\nIssuerName:%s", SubjectName, IssuerName);
*X509DataInfo = OutputStringBuf;
OutputStringBuf = NULL;
Status = EFI_SUCCESS;
ON_ERROR:
if (SubjectName != NULL) {
FreePool (SubjectName);
}
if (IssuerName != NULL) {
FreePool (IssuerName);
}
if (OutputStringBuf != NULL) {
FreePool (OutputStringBuf);
}
return Status;
}
/**
Read file based on the input file context.
@param[in] FileContext Pointer to the file context
@param[out] FileSize Returns the size of the file that was read
@param[out] File Dynamically allocated memory that contains a copy of the file
@retval EFI_SUCCESS Read file successfully.
@retval EFI_UNSUPPORTED File context is invalid or load file function return failed.
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory.
@retval EFI_NOT_FOUND Fail to get file info.
@retval Other Fail to get load file protocol instance or open/read file.
**/
STATIC
EFI_STATUS
ReadFile (
IN EM_FILE_CONTEXT *FileContext,
OUT UINTN *FileSize,
OUT UINT8 **File
)
{
EFI_FILE_HANDLE Dir;
EFI_FILE_HANDLE NewDir;
EFI_FILE_INFO *DirInfo;
EFI_STATUS Status;
UINTN BufferSize;
UINT8 *Buffer;
EFI_LOAD_FILE_PROTOCOL *LoadFile;
if (FileContext->IsLoadFile && !FileContext->IsBootLegacy) {
if (FileContext->Handle == NULL) {
return EFI_UNSUPPORTED;
}
Status = gBS->HandleProtocol (FileContext->Handle, &gEfiLoadFileProtocolGuid, (VOID**)&LoadFile);
if (EFI_ERROR (Status)) {
return Status;
}
HookOutputString ();
//
// Call LoadFile with the correct buffer size
//
BufferSize = 0;
Buffer = NULL;
Status = LoadFile->LoadFile (
LoadFile,
FileContext->DevicePath,
TRUE,
&BufferSize,
Buffer
);
if (Status == EFI_BUFFER_TOO_SMALL) {
Buffer = AllocatePool (BufferSize);
if (Buffer == NULL) {
RestoreOutputString ();
return EFI_OUT_OF_RESOURCES;
}
Status = LoadFile->LoadFile (
LoadFile,
FileContext->DevicePath,
TRUE,
&BufferSize,
Buffer
);
}
RestoreOutputString ();
if (EFI_ERROR (Status)) {
SafeFreePool ((VOID **) &Buffer);
return EFI_UNSUPPORTED;
}
} else {
Dir = FileContext->FHandle;
Status = Dir->Open (
Dir,
&NewDir,
FileContext->FileName,
EFI_FILE_READ_ONLY,
0
);
if (EFI_ERROR (Status)) {
return Status;
}
DirInfo = UtilityLibFileInfo (NewDir);
if (!DirInfo) {
NewDir->Close (NewDir);
return EFI_NOT_FOUND;
}
BufferSize = (UINTN) DirInfo->FileSize;
Buffer = AllocateZeroPool (BufferSize);
ASSERT (Buffer != NULL);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
SafeFreePool ((VOID **) &DirInfo);
Status = NewDir->Read (NewDir, &BufferSize, Buffer);
NewDir->Close (NewDir);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
}
*File = Buffer;
*FileSize = BufferSize;
return EFI_SUCCESS;
}
/**
Enroll a new X509 certificate into Variable.
@param[in] CallbackData Instance to control HII page.
@param[in] PrivateData The module's private data.
@param[in] VariableName Variable name of CA database.
@retval EFI_SUCCESS New X509 is enrolled successfully.
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
**/
STATIC
EFI_STATUS
EnrollX509toVariable (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN VOID *X509Data,
IN UINTN X509DataSize,
IN CHAR16 *VariableName,
IN EFI_GUID *CertGuid
)
{
EFI_STATUS Status;
EFI_SIGNATURE_LIST *CACert;
EFI_SIGNATURE_DATA *CACertData;
VOID *Data;
UINTN DataSize;
UINTN SigDataSize;
UINT32 Attr;
EFI_INPUT_KEY Key;
SigDataSize = 0;
DataSize = 0;
CACert = NULL;
CACertData = NULL;
Data = NULL;
DEBUG ((DEBUG_INFO, "CertGuid : %g\n", CertGuid));
if (IsGuidExist (CertGuid)) {
DEBUG ((DEBUG_INFO, "The same CertGuid is existed.\n"));
CallbackData->H2ODialog->ConfirmDialog (
DlgOk,
FALSE,
0,
NULL,
&Key,
L"The same CertGuid is existed.\n"
);
return EFI_SUCCESS;
}
SigDataSize = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + X509DataSize;
Data = AllocateZeroPool (SigDataSize);
if (Data == NULL) {
DEBUG ((DEBUG_INFO, "EFI_OUT_OF_RESOURCES.\n"));
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
//
// Fill Certificate Database parameters.
//
CACert = (EFI_SIGNATURE_LIST*) Data;
CACert->SignatureListSize = (UINT32) SigDataSize;
CACert->SignatureHeaderSize = 0;
CACert->SignatureSize = (UINT32) (sizeof(EFI_SIGNATURE_DATA) - 1 + X509DataSize);
CopyGuid (&CACert->SignatureType, &gEfiCertX509Guid);
CACertData = (EFI_SIGNATURE_DATA*) ((UINT8* ) CACert + sizeof (EFI_SIGNATURE_LIST));
CopyGuid (&CACertData->SignatureOwner, CertGuid);
CopyMem ((UINT8* ) (CACertData->SignatureData), X509Data, X509DataSize);
//
// Check if signature database entry has been already existed.
// If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
// new signature data to original variable
//
Attr = TLS_CA_CERTIFICATE_VARIABLE_BASE_ATTR;
Status = CommonGetVariable (
VariableName,
&gEfiTlsCaCertificateGuid,
&DataSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
Attr |= EFI_VARIABLE_APPEND_WRITE;
} else if (Status != EFI_NOT_FOUND) {
DEBUG ((DEBUG_INFO, "ERROR Status : %r.\n", Status));
goto ON_EXIT;
}
Status = CommonSetVariable(
VariableName,
&gEfiTlsCaCertificateGuid,
Attr,
SigDataSize,
Data
);
ON_EXIT:
if (Data != NULL) {
FreePool (Data);
}
DEBUG ((DEBUG_INFO, "EnrollX509toVariable Status : %r\n", Status));
return Status;
}
/**
Process PEM file to CERT
@param[in] CallbackData Instance to control HII page.
@param[in] BufferSize Buffer size of buffer.
@param[in] Buffer Buffer pointer to PEM file content.
@retval EFI_SUCCESS The operation succeeded.
@retval EFI_INVALID_PARAMETER The parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Required resources could not be allocated.
@retval EFI_NOT_READY CryptoServices protocol is not ready.
@retval EFI_ABORTED User cancel from dialog.
@retval others Error status from external functions.
**/
STATIC
EFI_STATUS
ProcessPemFile (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN UINTN BufferSize,
IN UINT8 *Buffer
)
{
EFI_STATUS Status;
EFI_INPUT_KEY Key;
CHAR16 *X509Info;
EFI_GUID CertGuid;
if (mCryptoServices == NULL) {
return EFI_NOT_READY;
}
//
// Generate information to user
//
X509Info = NULL;
DEBUG ((DEBUG_INFO, "DecodeX509ToSubjectAndIssuerName %08x %d\n", Buffer, BufferSize));
Status = DecodeX509ToSubjectAndIssuerName (
Buffer,
BufferSize,
&X509Info,
&CertGuid
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "ConfirmDialog OK %08x\n", CallbackData->H2ODialog));
CallbackData->H2ODialog->ConfirmDialog (
DlgOk,
FALSE,
0,
NULL,
&Key,
L"Certificate file is incorrect."
);
return EFI_INVALID_PARAMETER;
}
DEBUG ((DEBUG_INFO, "ConfirmDialog Yes or no\n"));
Status = CallbackData->H2ODialog->ConfirmDialog (
DlgYesNo,
FALSE,
0,
NULL,
&Key,
X509Info
);
FreePool (X509Info);
if (Key.UnicodeChar != CHAR_CARRIAGE_RETURN) {
return EFI_ABORTED;
}
//
// Insert certificate to variable
//
DEBUG ((DEBUG_INFO, "Import to variable %g Size: %d\n", &CertGuid, BufferSize));
Status = EnrollX509toVariable (CallbackData, Buffer, BufferSize, TLS_CA_CERTIFICATE_VARIABLE, &CertGuid);
return Status;
}
/**
Append file name to existing file name.
@param Str1 existing file name
@param Str2 file name to be appended
@return Allocate a new string to hold the appended result.
Caller is responsible to free the returned string.
**/
STATIC
CHAR16 *
AppendFileName (
IN CHAR16 *Str1,
IN CHAR16 *Str2
)
{
UINTN Size1;
UINTN Size2;
CHAR16 *Str;
CHAR16 *TmpStr;
CHAR16 *Ptr;
CHAR16 *LastSlash;
Size1 = StrSize (Str1);
Size2 = StrSize (Str2);
//
// Integer Overflow check
//
if (Size1 > Size1 + Size2 + sizeof (CHAR16) || Size2 > Size1 + Size2 + sizeof (CHAR16)) {
ASSERT (FALSE);
return NULL;
}
Str = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16));
ASSERT (Str != NULL);
if (Str == NULL) {
return NULL;
}
TmpStr = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16));
ASSERT (TmpStr != NULL);
if (TmpStr == NULL) {
return NULL;
}
StrCatS (Str, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), Str1);
if (!((*Str == '\\') && (*(Str + 1) == 0))) {
StrCatS (Str, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), L"\\");
}
StrCatS (Str, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), Str2);
Ptr = Str;
LastSlash = Str;
while (*Ptr != 0) {
if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '.' && *(Ptr + 3) == L'\\') {
//
// Convert "\Name\..\" to "\"
// DO NOT convert the .. if it is at the end of the string. This will
// break the .. behavior in changing directories.
//
//
// Use TmpStr as a backup, as StrCpy in BaseLib does not handle copy of two strings
// that overlap.
//
StrCpyS (TmpStr, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), Ptr + 3);
StrCpyS (LastSlash, (Size1 + Size2 + sizeof (CHAR16) - ((UINT8 *) LastSlash - (UINT8 *) Str)) / sizeof(CHAR16), TmpStr);
Ptr = LastSlash;
} else if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '\\') {
//
// Convert a "\.\" to a "\"
//
//
// Use TmpStr as a backup, as StrCpy in BaseLib does not handle copy of two strings
// that overlap.
//
StrCpyS (TmpStr, (Size1 + Size2 + sizeof (CHAR16))/sizeof(CHAR16), Ptr + 2);
StrCpyS (Ptr, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), TmpStr);
Ptr = LastSlash;
} else if (*Ptr == '\\') {
LastSlash = Ptr;
}
Ptr++;
}
FreePool (TmpStr);
return Str;
}
/**
Check if input file name matches with the file extension string or not.
@param[in] FileName Pointer to the file name
@param[in] FileExtensionStr Pointer to the file extension string
@retval TRUE Input file name matches with the file extension string.
@retval FALSE Input file name does not match with the file extension string.
**/
STATIC
BOOLEAN
IsFileExtensionMatched (
IN CHAR16 *FileName,
IN CHAR16 *FileExtensionStr
)
{
STATIC CHAR16 *UpperCaseStr = NULL;
STATIC CHAR16 *LowerCaseStr = NULL;
STATIC UINTN BufferLen = 0;
UINTN FileExtensionStrLen;
UINTN Index;
CHAR16 Char;
if (FileName == NULL || FileExtensionStr == NULL || *FileName == CHAR_NULL || *FileExtensionStr == CHAR_NULL) {
return FALSE;
}
//
// Allocate memory to store file extension string in upper case and lower case.
//
FileExtensionStrLen = StrLen (FileExtensionStr);
if (BufferLen < FileExtensionStrLen) {
SafeFreePool ((VOID **) &UpperCaseStr);
SafeFreePool ((VOID **) &LowerCaseStr);
UpperCaseStr = AllocatePool (FileExtensionStrLen * sizeof (CHAR16));
LowerCaseStr = AllocatePool (FileExtensionStrLen * sizeof (CHAR16));
BufferLen = FileExtensionStrLen;
}
if (UpperCaseStr == NULL || LowerCaseStr == NULL) {
BufferLen = 0;
return FALSE;
}
for (Index = 0; Index < FileExtensionStrLen; Index++) {
Char = FileExtensionStr[Index];
UpperCaseStr[Index] = (Char >= L'a' && Char <= L'z') ? ((Char - L'a') + L'A') : Char;
LowerCaseStr[Index] = (Char >= L'A' && Char <= L'Z') ? ((Char - L'A') + L'a') : Char;
}
//
// Check if file name contains the file extension name.
//
while (*FileName) {
for (Index = 0; Index < FileExtensionStrLen; Index++) {
if (FileName[Index] == CHAR_NULL) {
return FALSE;
}
if (FileName[Index] != LowerCaseStr[Index] &&
FileName[Index] != UpperCaseStr[Index]) {
break;
}
DEBUG ((DEBUG_INFO, "%c\n", FileName[Index]));
}
if (Index == FileExtensionStrLen) {
return TRUE;
}
FileName += 1;
}
return FALSE;
}
/**
Based on target file format, check if the input file is qualified or not.
@param[in] FileFormat Target file format
@param[in] FileInfo Pointer to the file info
@retval TRUE The input file is qualified.
@retval FALSE The input file is not qualified or input parameter is invalid.
**/
STATIC
BOOLEAN
IsFileQualified (
IN UINTN FileFormat,
IN EFI_FILE_INFO *FileInfo
)
{
DEBUG ((DEBUG_INFO, "%d IsFileQualified FileName %s %08x\n", FileFormat, FileInfo->FileName, FileInfo));
if (FileInfo == NULL) {
return FALSE;
}
if (FileFormat == EM_FILE_FORMAT_PEM) {
if (IsFileExtensionMatched (FileInfo->FileName, L".pem")
|| IsFileExtensionMatched (FileInfo->FileName, L".cer")
|| IsFileExtensionMatched (FileInfo->FileName, L".crt")) {
return TRUE;
}
}
return FALSE;
}
/**
Create Menu Entry for future use, make all types together
in order to reduce code size
@param MenuType Use this parameter to identify current
Menu type
@return A valid pointer pointing to the allocated memory pool for current menu entry
or NULL if it cannot allocate memory for current menu entry
**/
STATIC
ENROLL_MENU_ENTRY *
CreateFileMenuEntry (
VOID
)
{
ENROLL_MENU_ENTRY *MenuEntry;
UINTN ContextSize;
DEBUG ((DEBUG_INFO, "CreateFileMenuEntry\n"));
ContextSize = sizeof (BM_FILE_CONTEXT);
MenuEntry = AllocateZeroPool (sizeof (ENROLL_MENU_ENTRY));
if (NULL == MenuEntry) {
return MenuEntry;
}
MenuEntry->VariableContext = AllocateZeroPool (ContextSize);
if (NULL == MenuEntry->VariableContext) {
SafeFreePool ((VOID**) &MenuEntry);
MenuEntry = NULL;
return MenuEntry;
}
MenuEntry->Signature = EM_MENU_ENTRY_SIGNATURE;
MenuEntry->ContextSelection = EM_FILE_CONTEXT_SELECT;
return MenuEntry;
}
/**
Create Menu Entry for future use, make all types together
in order to reduce code size
@param MenuType Use this parameter to identify current
Menu type
@return A valid pointer pointing to the allocated memory pool for current menu entry
or NULL if it cannot allocate memory for current menu entry
**/
STATIC
ENROLL_MENU_ENTRY *
CreateVariableMenuEntry (
VOID
)
{
ENROLL_MENU_ENTRY *MenuEntry;
UINTN ContextSize;
DEBUG ((DEBUG_INFO, "CreateVariableMenuEntry\n"));
ContextSize = sizeof (EM_CERTIFICATE_CONTEXT);
MenuEntry = AllocateZeroPool (sizeof (ENROLL_MENU_ENTRY));
if (NULL == MenuEntry) {
return MenuEntry;
}
MenuEntry->VariableContext = AllocateZeroPool (ContextSize);
if (NULL == MenuEntry->VariableContext) {
SafeFreePool ((VOID**) &MenuEntry);
MenuEntry = NULL;
return MenuEntry;
}
MenuEntry->Signature = EM_MENU_ENTRY_SIGNATURE;
// MenuEntry->ContextSelection = EM_FILE_CONTEXT_SELECT;
return MenuEntry;
}
/**
Find files under current directory
All files and sub-directories in current directory
will be stored in DirectoryMenu for future use.
@param FileOption Pointer for Dir to explore
@retval TRUE Get files from current dir successfully
@retval FALSE Can't get files from current dir
**/
STATIC
EFI_STATUS
FindFiles (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN ENROLL_MENU_ENTRY *MenuEntry
)
{
EFI_FILE_HANDLE NewDir;
EFI_FILE_HANDLE Dir;
EFI_FILE_INFO *DirInfo;
UINTN BufferSize;
UINTN DirBufferSize;
ENROLL_MENU_ENTRY *NewMenuEntry;
EM_FILE_CONTEXT *FileContext;
EM_FILE_CONTEXT *NewFileContext;
UINTN Pass;
EFI_STATUS Status;
UINTN OptionNumber;
DEBUG ((DEBUG_INFO, "FindFiles File format %d\n", CallbackData->EmFileFormat));
FileContext = (EM_FILE_CONTEXT *) MenuEntry->VariableContext;
Dir = FileContext->FHandle;
OptionNumber = 0;
//
// Open current directory to get files from it
//
Status = Dir->Open (
Dir,
&NewDir,
FileContext->FileName,
EFI_FILE_READ_ONLY,
0
);
if (!FileContext->IsRoot) {
Dir->Close (Dir);
}
if (EFI_ERROR (Status)) {
return Status;
}
DirInfo = UtilityLibFileInfo (NewDir);
if (!DirInfo) {
return EFI_NOT_FOUND;
}
if (!(DirInfo->Attribute & EFI_FILE_DIRECTORY)) {
return EFI_INVALID_PARAMETER;
}
FileContext->DevicePath = FileDevicePath (
FileContext->Handle,
FileContext->FileName
);
DirBufferSize = sizeof (EFI_FILE_INFO) + 1024;
DirInfo = AllocateZeroPool (DirBufferSize);
if (!DirInfo) {
return EFI_OUT_OF_RESOURCES;
}
//
// Get all files in current directory
// Pass 1 to get Directories
// Pass 2 to get files that are EFI images
//
for (Pass = 1; Pass <= 2; Pass++) {
NewDir->SetPosition (NewDir, 0);
DEBUG ((DEBUG_INFO, "Pass %d\n", Pass));
for (;;) {
BufferSize = DirBufferSize;
Status = NewDir->Read (NewDir, &BufferSize, DirInfo);
if (EFI_ERROR (Status) || BufferSize == 0) {
break;
}
if ((DirInfo->Attribute & EFI_FILE_DIRECTORY && Pass == 2) ||
(!(DirInfo->Attribute & EFI_FILE_DIRECTORY) && Pass == 1)
) {
//
// Pass 1 is for Directories
// Pass 2 is for file names
//
continue;
}
DEBUG ((DEBUG_INFO, "!!! %d FileName %s %d\n", Pass, DirInfo->FileName, DirInfo->Attribute));
if (!(DirInfo->Attribute & EFI_FILE_DIRECTORY || IsFileQualified (CallbackData->EmFileFormat, DirInfo))) {
//
// Skip file unless it is a directory entry or file is qualified in current format.
//
DEBUG ((DEBUG_INFO, " Skiped\n", Pass, DirInfo->FileName, DirInfo->Attribute));
continue;
} else if ((DirInfo->Attribute & EFI_FILE_DIRECTORY) && (StrLen (DirInfo->FileName) == 1) && (DirInfo->FileName[0] == '.')) {
//
// Skip . folder
//
DEBUG ((DEBUG_INFO, " Skiped\n", Pass, DirInfo->FileName, DirInfo->Attribute));
continue;
}
NewMenuEntry = CreateFileMenuEntry ();
if (NULL == NewMenuEntry) {
return EFI_OUT_OF_RESOURCES;
}
NewFileContext = (EM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
NewFileContext->Handle = FileContext->Handle;
NewFileContext->FileName = AppendFileName (
FileContext->FileName,
DirInfo->FileName
);
if (NewFileContext->FileName == NULL) {
return EFI_OUT_OF_RESOURCES;
}
NewFileContext->FHandle = NewDir;
NewFileContext->DevicePath = FileDevicePath (
NewFileContext->Handle,
NewFileContext->FileName
);
NewMenuEntry->HelpString = NULL;
MenuEntry->DisplayStringToken = FileListGetStrTokenFromDepository (
CallbackData,
mSecureBootFileOptionStrDepository
);
NewFileContext->IsDir = (BOOLEAN) ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY);
if (NewFileContext->IsDir) {
BufferSize = StrLen (DirInfo->FileName) * 2 + 6;
NewMenuEntry->DisplayString = AllocateZeroPool (BufferSize);
UnicodeSPrint (
NewMenuEntry->DisplayString,
BufferSize,
L"<%s>",
DirInfo->FileName
);
} else {
NewMenuEntry->DisplayString = AllocateCopyPool (StrSize (DirInfo->FileName), DirInfo->FileName);
}
NewFileContext->IsRoot = FALSE;
NewFileContext->IsLoadFile = FALSE;
NewFileContext->IsRemovableMedia = FALSE;
NewMenuEntry->OptionNumber = OptionNumber;
OptionNumber++;
InsertTailList (&mFileListDirectoryMenu.Head, &NewMenuEntry->Link);
}
}
mFileListDirectoryMenu.MenuNumber = OptionNumber;
SafeFreePool ((VOID**) &DirInfo);
return EFI_SUCCESS;
}
/**
Use this routine to get one particular menu entry in specified menu
@param MenuOption The menu that we will search
@param MenuNumber The menunubmer that we want
@return The desired menu entry
**/
STATIC
ENROLL_MENU_ENTRY *
GetMenuEntry (
ENROLL_MENU_OPTION *MenuOption,
UINTN MenuNumber
)
{
ENROLL_MENU_ENTRY *NewMenuEntry;
UINTN Index;
LIST_ENTRY *List;
if (MenuNumber >= MenuOption->MenuNumber) {
return NULL;
}
List = MenuOption->Head.ForwardLink;
for (Index = 0; Index < MenuNumber; Index++) {
List = List->ForwardLink;
}
NewMenuEntry = CR (List, ENROLL_MENU_ENTRY, Link, EM_MENU_ENTRY_SIGNATURE);
return NewMenuEntry;
}
/**
Get the form ID of enroll menu based on secure boot manager state.
@param[in] SecureBootMgrState Secure boot manager state
@return The form ID of enroll menu or zero if not found.
**/
STATIC
EFI_FORM_ID
FileListGetMenuFormId (
IN FILE_LIST_STATE FileListState
)
{
switch (FileListState) {
case TlsForm:
return FORM_ID_IMPORT_CERTIFICATE_FILE;
case VariableForm:
return FORM_ID_MANAGE_CERTIFICATE_FILE;
default:
return FileListStateMax;
}
}
/**
Update the Enroll Menu page.
@param CallbackData Pointer to TLS_CERT_MANAGER_CALLBACK_DATA instance
@param MenuOption Pointer to menu options to display.
**/
STATIC
VOID
UpdateEnrollMenuPage (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN ENROLL_MENU_OPTION *MenuOption
)
{
UINTN Index;
ENROLL_MENU_ENTRY *NewMenuEntry;
EM_FILE_CONTEXT *NewFileContext;
EFI_FORM_ID FormId;
VOID *StartOpCodeHandle;
VOID *EndOpCodeHandle;
EFI_IFR_GUID_LABEL *StartLabel;
EFI_IFR_GUID_LABEL *EndLabel;
UINT16 NewQuestionId;
NewMenuEntry = NULL;
NewFileContext = NULL;
FormId = FileListGetMenuFormId (CallbackData->FileListState);
ASSERT (FormId != 0);
if (FormId == 0) {
return;
}
DEBUG ((DEBUG_INFO, "UpdateEnrollMenuPage %04x %d %d\n", FormId, CallbackData->FileListState, CallbackData->EmFileFormat));
//
// Allocate space for creation of UpdateData Buffer
//
StartOpCodeHandle = HiiAllocateOpCodeHandle ();
ASSERT (StartOpCodeHandle != NULL);
EndOpCodeHandle = HiiAllocateOpCodeHandle ();
ASSERT (EndOpCodeHandle != NULL);
//
// Create Hii Extend Label OpCode as the start opcode
//
StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
StartLabel->Number = LABEL_ID_FILE_LIST_START;
//
// Create Hii Extend Label OpCode as the end opcode
//
EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
EndLabel->Number = LABEL_ID_FILE_LIST_END;
DEBUG ((DEBUG_INFO, "Menu number %d\n", MenuOption->MenuNumber));
for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
NewMenuEntry = GetMenuEntry (MenuOption, Index);
NewFileContext = (EM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
DEBUG ((DEBUG_INFO, "Index : %d\n", Index));
if (CallbackData->FileListState == VariableForm) {
if (CallbackData->EmFileFormat == EM_FILE_FORMAT_DEL_VAR) {
NewQuestionId = (UINT16)(ENROLL_VAR_DELETE_OPTION_OFFSET + Index);
DEBUG ((DEBUG_INFO, "Create string token to delete certificate : %04x\n", NewQuestionId));
} else if (CallbackData->EmFileFormat == EM_FILE_FORMAT_EXP_VAR) {
NewQuestionId = (UINT16)(ENROLL_VAR_EXPORT_OPTION_OFFSET + Index);
DEBUG ((DEBUG_INFO, "Create string token to export certificate : %04x\n", NewQuestionId));
} else {
DEBUG ((DEBUG_INFO, "Unknow status\n"));
NewQuestionId = 0xFFFF;
}
DEBUG ((DEBUG_INFO, "HiiCreateActionOpCode VariableForm %04x\n", NewQuestionId));
HiiCreateActionOpCode (
StartOpCodeHandle,
(UINT16) NewQuestionId,
NewMenuEntry->DisplayStringToken,
NewMenuEntry->HelpStringToken,
EFI_IFR_FLAG_CALLBACK,
0
);
}else if (CallbackData->FileListState == TlsForm || NewFileContext->IsDir || NewFileContext->IsLoadFile) {
DEBUG ((DEBUG_INFO, "ENROLL_FILE_OPTION_OFFSET\n"));
HiiCreateActionOpCode (
StartOpCodeHandle,
(UINT16) (ENROLL_FILE_OPTION_OFFSET + Index),
NewMenuEntry->DisplayStringToken,
STRING_TOKEN (STR_BLANK_STRING),
EFI_IFR_FLAG_CALLBACK,
0
);
}
}
DEBUG ((DEBUG_INFO, "Update form ID : %04x\n", FormId));
HiiUpdateForm (
CallbackData->RegisteredHandle,
NULL,
FormId,
StartOpCodeHandle,
EndOpCodeHandle
);
HiiFreeOpCodeHandle (StartOpCodeHandle);
HiiFreeOpCodeHandle (EndOpCodeHandle);
}
/**
Create string tokens for a menu from its help strings and display strings
@param HiiHandle Hii Handle of the package to be updated.
@param MenuOption The Menu whose string tokens need to be created
@retval EFI_SUCCESS string tokens created successfully
@retval Other contain some errors
**/
STATIC
EFI_STATUS
CreateMenuStringToken (
IN EFI_HII_HANDLE HiiHandle,
IN ENROLL_MENU_OPTION *MenuOption
)
{
ENROLL_MENU_ENTRY *NewMenuEntry;
UINTN Index;
for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
NewMenuEntry = GetMenuEntry (MenuOption, Index);
DEBUG ((DEBUG_INFO, "SetString : %s\n", NewMenuEntry->DisplayString));
NewMenuEntry->DisplayStringToken = HiiSetString (
HiiHandle,
NewMenuEntry->DisplayStringToken,
NewMenuEntry->DisplayString,
NULL
);
if (NULL == NewMenuEntry->HelpString) {
NewMenuEntry->HelpStringToken = NewMenuEntry->DisplayStringToken;
} else {
NewMenuEntry->HelpStringToken = HiiSetString (
HiiHandle,
NewMenuEntry->HelpStringToken,
NewMenuEntry->HelpString,
NULL
);
}
}
return EFI_SUCCESS;
}
/**
Free resources allocated in Allocate Rountine
@param FreeMenu Menu to be freed
**/
STATIC
VOID
FreeMenu (
ENROLL_MENU_OPTION *FreeMenu
)
{
ENROLL_MENU_ENTRY *MenuEntry;
while (!IsListEmpty (&FreeMenu->Head)) {
MenuEntry = CR (
FreeMenu->Head.ForwardLink,
ENROLL_MENU_ENTRY,
Link,
EM_MENU_ENTRY_SIGNATURE
);
RemoveEntryList (&MenuEntry->Link);
DestroyMenuEntry (MenuEntry);
}
}
/**
Find file systems for current Extensible Firmware
Including Handles that support Simple File System
protocol, Load File protocol.
Building up the FileSystem Menu for user selection
All file system will be stored in FsOptionMenu
for future use.
@param CallbackData BMM context data
@retval EFI_SUCCESS Success find the file system
@retval EFI_OUT_OF_RESOURCES Can not create menu entry
**/
STATIC
EFI_STATUS
FindFileSystem (
VOID
)
{
UINTN NoBlkIoHandles;
UINTN NoSimpleFsHandles;
EFI_HANDLE *BlkIoHandle;
EFI_HANDLE *SimpleFsHandle;
UINT16 *VolumeLabel;
EFI_BLOCK_IO_PROTOCOL *BlkIo;
UINTN Index;
EFI_STATUS Status;
ENROLL_MENU_ENTRY *MenuEntry;
EM_FILE_CONTEXT *FileContext;
UINT16 *TempStr;
UINTN OptionNumber;
VOID *Buffer;
BOOLEAN RemovableMedia;
NoSimpleFsHandles = 0;
OptionNumber = 0;
InitializeListHead (&mFileListFsOptionMenu.Head);
//
// Locate Handles that support BlockIo protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiBlockIoProtocolGuid,
NULL,
&NoBlkIoHandles,
&BlkIoHandle
);
if (!EFI_ERROR (Status)) {
for (Index = 0; Index < NoBlkIoHandles; Index++) {
Status = gBS->HandleProtocol (
BlkIoHandle[Index],
&gEfiBlockIoProtocolGuid,
(VOID **) &BlkIo
);
if (EFI_ERROR (Status)) {
continue;
}
//
// Issue a dummy read to trigger reinstall of BlockIo protocol for removable media
//
if (BlkIo->Media->RemovableMedia) {
Buffer = AllocateZeroPool (BlkIo->Media->BlockSize);
if (NULL == Buffer) {
SafeFreePool ((VOID**) &BlkIoHandle);
return EFI_OUT_OF_RESOURCES;
}
BlkIo->ReadBlocks (
BlkIo,
BlkIo->Media->MediaId,
0,
BlkIo->Media->BlockSize,
Buffer
);
SafeFreePool ((VOID**) &Buffer);
}
}
SafeFreePool ((VOID**) &BlkIoHandle);
}
//
// Locate Handles that support Simple File System protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NoSimpleFsHandles,
&SimpleFsHandle
);
if (!EFI_ERROR (Status)) {
//
// Find all the instances of the File System prototocol
//
for (Index = 0; Index < NoSimpleFsHandles; Index++) {
Status = gBS->HandleProtocol (
SimpleFsHandle[Index],
&gEfiBlockIoProtocolGuid,
(VOID **) &BlkIo
);
if (EFI_ERROR (Status)) {
//
// If no block IO exists assume it's NOT a removable media
//
RemovableMedia = FALSE;
} else {
//
// If block IO exists check to see if it's remobable media
//
RemovableMedia = BlkIo->Media->RemovableMedia;
}
//
// Allocate pool for this load option
//
DEBUG ((DEBUG_INFO, "FindFileSystem CreateFileMenuEntry\n"));
MenuEntry = CreateFileMenuEntry ();
if (NULL == MenuEntry) {
SafeFreePool ((VOID**) &SimpleFsHandle);
return EFI_OUT_OF_RESOURCES;
}
FileContext = (EM_FILE_CONTEXT *) MenuEntry->VariableContext;
FileContext->Handle = SimpleFsHandle[Index];
MenuEntry->OptionNumber = Index;
FileContext->FHandle = UtilityLibOpenRoot (FileContext->Handle);
if (!FileContext->FHandle) {
DestroyMenuEntry (MenuEntry);
continue;
}
MenuEntry->HelpString = ConvertDevicePathToText (DevicePathFromHandle (FileContext->Handle), FALSE, TRUE);
FileContext->Info = UtilityLibFileSystemVolumeLabelInfo (FileContext->FHandle);
FileContext->FileName = UtilityLibStrDuplicate (L"\\");
if (FileContext->FileName == NULL) {
DestroyMenuEntry (MenuEntry);
SafeFreePool ((VOID**) &SimpleFsHandle);
return EFI_OUT_OF_RESOURCES;
}
FileContext->DevicePath = FileDevicePath (
FileContext->Handle,
FileContext->FileName
);
FileContext->IsDir = TRUE;
FileContext->IsRoot = TRUE;
FileContext->IsRemovableMedia = FALSE;
FileContext->IsLoadFile = FALSE;
//
// Get current file system's Volume Label
//
if (FileContext->Info == NULL) {
VolumeLabel = L"NO FILE SYSTEM INFO";
} else {
VolumeLabel = FileContext->Info->VolumeLabel;
if (*VolumeLabel == 0x0000) {
VolumeLabel = L"NO VOLUME LABEL";
}
}
TempStr = MenuEntry->HelpString;
MenuEntry->DisplayString = AllocateZeroPool (MAX_CHAR);
ASSERT (MenuEntry->DisplayString != NULL);
UnicodeSPrint (
MenuEntry->DisplayString,
MAX_CHAR,
L"%s, [%s]",
VolumeLabel,
TempStr
);
OptionNumber++;
DEBUG ((DEBUG_INFO, "FindFileSystem InsertTailList %s\n", MenuEntry->DisplayString));
InsertTailList (&mFileListFsOptionMenu.Head, &MenuEntry->Link);
}
}
if (NoSimpleFsHandles != 0) {
SafeFreePool ((VOID**) &SimpleFsHandle);
}
//
// Remember how many file system options are here
//
mFileListFsOptionMenu.MenuNumber = OptionNumber;
return EFI_SUCCESS;
}
/**
Update the file explower page with the certificate list from Certificate store variable.
@param[in] CallbackData BSECURE_BOOT_MANAGER_CALLBACK_DATA instance
@param[in] KeyValue Key value to identify the type of data to expect.
@retval TRUE Inform the caller to create a callback packet to exit file explorer.
@retval FALSE Indicate that there is no need to exit file explorer.
**/
STATIC
BOOLEAN
UpdateEmCertFileExplorer (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN UINT16 KeyValue
)
{
EFI_STATUS Status;
UINT16 FileOptionMask;
ENROLL_MENU_ENTRY *NewMenuEntry;
UINT16 CertIndex;
FileOptionMask = (UINT16) (ENROLL_VAR_OPTION_CMD_MASK & KeyValue);
NewMenuEntry = NULL;
CertIndex = 0xFFFF;
if (FileOptionMask == ENROLL_VAR_DELETE_OPTION_OFFSET) {
DEBUG ((DEBUG_INFO, "DELETE certificate from variable %04x\n", KeyValue));
CertIndex = KeyValue - ENROLL_VAR_DELETE_OPTION_OFFSET;
} else if (FileOptionMask == ENROLL_VAR_EXPORT_OPTION_OFFSET) {
DEBUG ((DEBUG_INFO, "EXPORT certificate from variable %04x\n", KeyValue));
CertIndex = KeyValue - ENROLL_VAR_EXPORT_OPTION_OFFSET;
} else {
DEBUG ((DEBUG_INFO, "UpdateEmCertFileExplorer %d %04x\n", CallbackData->EmDisplayContext, KeyValue));
}
DEBUG ((DEBUG_INFO, "ProcessCertificatesFromCertStore \n" ));
Status = ProcessCertificatesFromCertStore (CallbackData, CertIndex, NULL);
DEBUG ((DEBUG_INFO, "CreateMenuStringToken \n" ));
CreateMenuStringToken (CallbackData->RegisteredHandle, &mFileListCertFileMenu);
UpdateEnrollMenuPage (CallbackData, &mFileListCertFileMenu);
return FALSE;
}
/**
Update the file explower page with the refershed file system.
@param CallbackData TLS_CERT_MANAGER_CALLBACK_DATA instance
@param KeyValue Key value to identify the type of data to expect.
@retval TRUE Inform the caller to create a callback packet to exit file explorer.
@retval FALSE Indicate that there is no need to exit file explorer.
**/
STATIC
BOOLEAN
UpdateEmFileExplorer (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN UINT16 KeyValue
)
{
EFI_STATUS Status;
UINT16 FileOptionMask;
ENROLL_MENU_ENTRY *NewMenuEntry;
EM_FILE_CONTEXT *NewFileContext;
FileOptionMask = (UINT16) (ENROLL_FILE_OPTION_MASK & KeyValue);
NewMenuEntry = NULL;
DEBUG ((DEBUG_INFO, "UpdateEmFileExplorer %d %04x\n", CallbackData->EmDisplayContext, KeyValue));
if (EM_UNKNOWN_CONTEXT == CallbackData->EmDisplayContext) {
FindFileSystem ();
CreateMenuStringToken (CallbackData->RegisteredHandle, &mFileListFsOptionMenu);
UpdateEnrollMenuPage (CallbackData, &mFileListFsOptionMenu);
CallbackData->EmDisplayContext = EM_FILE_SYSTEM;
} else {
if (EM_FILE_SYSTEM == CallbackData->EmDisplayContext) {
NewMenuEntry = GetMenuEntry (&mFileListFsOptionMenu, FileOptionMask);
} else if (EM_DIRECTORY == CallbackData->EmDisplayContext) {
NewMenuEntry = GetMenuEntry (&mFileListDirectoryMenu, FileOptionMask);
}
if (NewMenuEntry == NULL) {
return FALSE;
}
NewFileContext = (EM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
if (NewFileContext->IsDir ) {
CallbackData->EmDisplayContext = EM_DIRECTORY;
RemoveEntryList (&NewMenuEntry->Link);
FreeMenu (&mFileListDirectoryMenu);
FileListReclaimStrDepository (mSecureBootFileOptionStrDepository);
Status = FindFiles (CallbackData, NewMenuEntry);
DestroyMenuEntry (NewMenuEntry);
if (EFI_ERROR (Status)) {
return TRUE;
}
CreateMenuStringToken (CallbackData->RegisteredHandle, &mFileListDirectoryMenu);
UpdateEnrollMenuPage (CallbackData, &mFileListDirectoryMenu);
} else {
//
// Process file to insert to Variable.
//
ProcessFile (CallbackData, NewMenuEntry, NewFileContext);
}
}
return FALSE;
}
/**
Save data to variable(gEfiTlsCaCertificateGuid, TLS_CA_CERTIFICATE_VARIABLE)
@param[in] Data Data
@param[in] DataSize size of data, in byte.
@retval Status Status from SetVariable.
**/
STATIC
EFI_STATUS
UpdateCaCertificateVariable (
IN UINT8 *Data,
IN UINTN DataSize
)
{
DEBUG ((DEBUG_INFO, "UpdateCaCertificateVariable %08x %d\n", Data, DataSize));
if (DataSize == 0) {
Data = NULL;
}
return CommonSetVariable(
TLS_CA_CERTIFICATE_VARIABLE,
&gEfiTlsCaCertificateGuid,
TLS_CA_CERTIFICATE_VARIABLE_BASE_ATTR,
DataSize,
Data
);
}
/**
Get data to variable(gEfiTlsCaCertificateGuid, TLS_CA_CERTIFICATE_VARIABLE)
@param[out] Data Data pointer to a buffer pointer.
@param[out] DataSize Size pointer parameter, function will return how many bytes get from variable.
@retval EFI_SUCCESS Process successful.
@retval EFI_OUT_OF_RESOURCES Allocate memory failed.
@retval Status Status from GetVariable.
**/
STATIC
EFI_STATUS
GetCaCertificateVariable (
OUT UINT8 **Data,
OUT UINTN *DataSize
)
{
EFI_STATUS Status;
CHAR16 *VariableName;
UINTN TmpDataSize;
UINT8 *TmpDataPtr;
VariableName = TLS_CA_CERTIFICATE_VARIABLE;
*Data = NULL;
TmpDataSize = 0;
Status = CommonGetVariable(
VariableName,
&gEfiTlsCaCertificateGuid,
&TmpDataSize,
NULL
);
if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
DEBUG ((DEBUG_INFO, "GetCaCertificateVariable %r\n", Status ));
return Status;
}
DEBUG ((DEBUG_INFO, "GetCaCertificateVariable size: %d\n", TmpDataSize ));
TmpDataPtr = AllocateZeroPool (TmpDataSize);
if (TmpDataPtr == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = CommonGetVariable (
VariableName,
&gEfiTlsCaCertificateGuid,
&TmpDataSize,
(VOID *) TmpDataPtr
);
if (EFI_ERROR (Status)) {
FreePool (TmpDataPtr);
return Status;
}
*Data = TmpDataPtr;
*DataSize = TmpDataSize;
return EFI_SUCCESS;
}
/**
Dump information from certificate to HII element.
@param[in] CertList Certificate list
@param[out] NewEmEntry HII element.
@retval EFI_SUCCESS The operation succeeded.
@retval Status Status from other functions.
**/
STATIC
EFI_STATUS
DumpCertificate (
IN EFI_SIGNATURE_LIST *CertList,
OUT ENROLL_MENU_ENTRY *NewEmEntry
)
{
EFI_STATUS Status;
EFI_SIGNATURE_DATA *CertRecord;
UINTN CertCount;
UINT8 *TmpCert;
UINTN TmpCertSize;
CHAR16 *SubjectNameStr;
CHAR16 *IssuerNameStr;
EFI_GUID CertGuid;
if (mCryptoServices == NULL) {
return EFI_NOT_READY;
}
CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
CertRecord = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
TmpCert = CertRecord->SignatureData;
TmpCertSize = CertList->SignatureSize - sizeof(EFI_SIGNATURE_DATA) + 1;
//
// Display GUID and help
//
ZeroMem (&CertGuid, sizeof (EFI_GUID));
Status = DumpCaCertificate (TmpCert, TmpCertSize, &SubjectNameStr, &IssuerNameStr, &CertGuid);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, " X509 parse failed.\n", &CertGuid, SubjectNameStr, IssuerNameStr));
return Status;
}
DEBUG ((DEBUG_INFO, " %g\n SubjectName:%s\n IssuerName:%s\n", &CertGuid, SubjectNameStr, IssuerNameStr));
NewEmEntry->DisplayString = AllocateZeroPool (MAX_CHAR);
if (NewEmEntry->DisplayString == NULL) {
return EFI_OUT_OF_RESOURCES;
}
NewEmEntry->HelpString = AllocateZeroPool (MAX_CHAR);
if (NewEmEntry->HelpString == NULL) {
FreePool (NewEmEntry->DisplayString);
NewEmEntry->DisplayString = NULL;
return EFI_OUT_OF_RESOURCES;
}
UnicodeSPrint (NewEmEntry->DisplayString, MAX_CHAR, L"%s", SubjectNameStr);
UnicodeSPrint (NewEmEntry->HelpString, MAX_CHAR, L"SubjectName:%s\nIssuerName:%s", SubjectNameStr, IssuerNameStr);
return EFI_SUCCESS;
}
/**
Find the certificate from CERT store variable.
@param[in] Data Certificate store variable content.
@param[in] DataSize Certificate store variable content size.
@param[out] SignatureType Certificate type, refer GUIDs in MdePkg/Include/Guid/ImageAuthentication.h
@param[out] CertGuid The first certificate GUID of this X509 certificate list, if this list is not type of X509, it will be NULL.
@param[out] CertLength Length of X509 certificate list.
@retval EFI_SUCCESS The operation succeeded, please check SignatureType to avoid parse type is not gEfiCertX509Guid of data.
@retval EFI_NOT_FOUND Not found certificate.
@retval EFI_INVALID_PARAMETER Parameters is not valid.
**/
STATIC
EFI_STATUS
NextCertListInCertStore (
IN UINT8 *Data,
IN UINTN DataSize,
OUT EFI_GUID **SignatureType,
OUT EFI_GUID **CertGuid,
OUT UINTN *CertLength
)
{
EFI_SIGNATURE_LIST *CertList;
UINTN ItemDataSize;
UINTN CertSize;
EFI_SIGNATURE_DATA *CertRecord;
//
// Check parameters
//
if ((Data == NULL) || (DataSize == 0) || (SignatureType == NULL) || (CertGuid == NULL) || (CertLength == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Check validation of the certificate list.
//
ItemDataSize = DataSize;
CertList = (EFI_SIGNATURE_LIST *) Data;
if (ItemDataSize < CertList->SignatureListSize) {
return EFI_NOT_FOUND;
}
CertSize = 0;
*SignatureType = &CertList->SignatureType;
*CertGuid = NULL;
*CertLength = CertList->SignatureListSize;
if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
CertRecord = (EFI_SIGNATURE_DATA *)(CertList + 1);
*CertGuid = &CertRecord->SignatureOwner;
}
return EFI_SUCCESS;
}
/**
Check cert GUID is exist in CERT variable to avoid the same certificate import many times.
@param[in] CertGuid Target of certificate GUID.
@retval TRUE Exist.
@retval FALSE Not exist.
**/
STATIC
BOOLEAN
IsGuidExist (
IN EFI_GUID *TargetCertGuid
)
{
EFI_STATUS Status;
UINT8 *Data;
UINTN DataSize;
UINTN ItemDataSize;
UINT8 *TmpDataPtr;
EFI_GUID *SignatureType;
EFI_GUID *CertGuid;
UINTN CertListSize;
BOOLEAN IsExist;
DataSize = 0;
Data = NULL;
Status = GetCaCertificateVariable (&Data, &DataSize);
if (EFI_ERROR (Status)) {
return FALSE;
}
ItemDataSize = DataSize;
TmpDataPtr = Data;
IsExist = FALSE;
while (ItemDataSize > 0) {
Status = NextCertListInCertStore (TmpDataPtr, ItemDataSize, &SignatureType, &CertGuid, &CertListSize);
DEBUG ((DEBUG_INFO, "IsGuidExist %r %g\n", Status, CertGuid ));
if (EFI_ERROR (Status)) {
break;
}
//
// Push ptr to next CertList.
//
TmpDataPtr = TmpDataPtr + CertListSize;
ItemDataSize = ItemDataSize - CertListSize;
if (!CompareGuid (SignatureType, &gEfiCertX509Guid)) {
DEBUG ((DEBUG_INFO, "Not X509 certificate list\n"));
continue;
}
if (!CompareGuid (TargetCertGuid, CertGuid)) {
continue;
}
IsExist = TRUE;
}
if (Data != NULL) {
FreePool (Data);
}
return IsExist;
}
/**
Delete select index of certificate from CaCertificate variable.
@param[in] CallbackData Instance of HII.
@param[in] SelectIndex Index of cert in the CERT store.
@retval EFI_SUCCESS The operation succeeded.
@retval EFI_NOT_FOUND Selected index is not found in current MenuList.
@retval EFI_ABORTED User aborted the process from dialog.
@retval other Status from other functions.
**/
STATIC
EFI_STATUS
DeleteCertificateFromCertStore (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN UINT16 SelectIndex
)
{
EFI_STATUS Status;
UINT8 *Data;
UINTN DataSize;
EM_CERTIFICATE_CONTEXT *CertContext;
ENROLL_MENU_ENTRY *SelectMenuEntry;
EFI_INPUT_KEY Key;
DataSize = 0;
Data = NULL;
DEBUG ((DEBUG_INFO, "DeleteCertificateFromCertStore \n" ));
Status = GetCaCertificateVariable (&Data, &DataSize);
if (EFI_ERROR (Status)) {
return Status;
}
/// Find certificate offset and length in variable.
SelectMenuEntry = GetMenuEntry (&mFileListCertFileMenu, SelectIndex);
if (SelectMenuEntry == NULL) {
DEBUG ((DEBUG_INFO, "Selected index not found \n" ));
return EFI_NOT_FOUND;
}
DEBUG ((DEBUG_INFO, "Delete certificate ConfirmDialog Yes or no\n"));
Status = CallbackData->H2ODialog->ConfirmDialog (
DlgYesNo,
FALSE,
0,
NULL,
&Key,
L"Confirm to delete certificate?"
);
if (Key.UnicodeChar != CHAR_CARRIAGE_RETURN) {
DEBUG ((DEBUG_INFO, "User canceled.\n" ));
return EFI_ABORTED;
}
CertContext = SelectMenuEntry->VariableContext;
DEBUG ((DEBUG_INFO, "Variable Offset : %08x Length : %d %d\n", CertContext->VariableOffset, CertContext->CertLength, CertContext->VariableLengthToEnd));
if ((CertContext->VariableOffset == 0) && (CertContext->VariableLengthToEnd == 0)) {
/// Do nothing.
} else {
CopyMem (
(VOID *) (Data + CertContext->VariableOffset),
(VOID *) (Data + CertContext->VariableOffset + CertContext->CertLength),
CertContext->VariableLengthToEnd
);
}
//
// Update certificate variable.
//
UpdateCaCertificateVariable (Data, DataSize - CertContext->CertLength);
FreePool (Data);
DataSize = 0;
Data = NULL;
return EFI_SUCCESS;
}
/**
Process certificate files from CERT store variable.
If the OutputFileName is NULL.
If caller select the index of SelectIndex, this process will delete certificate from variable.
@param[in] CallbackData Instance of HII.
@param[in] SelectIndex Index of cert in the CERT store.
@param[in] OutputFileName Output file name, not support output yet.
If this is NULL, the certificate that selected by will be delete from variable store.
@retval EFI_SUCCESS The operation succeeded.
@retval EFI_UNSUPPORTED Operation not supported.
@retval Status Status from other functions.
**/
STATIC
EFI_STATUS
ProcessCertificatesFromCertStore (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN UINT16 SelectIndex,
IN CHAR16 *OutputFileName
)
{
EFI_STATUS Status;
UINT8 *Data;
UINTN DataSize;
UINTN ItemDataSize;
UINTN Index;
UINT8 *TmpDataPtr;
UINT8 *CertPtr;
EFI_GUID *SignatureType;
EFI_GUID *CertGuid;
UINTN CertListSize;
UINTN VariableOffset;
ENROLL_MENU_ENTRY *NewMenuEntry;
EM_CERTIFICATE_CONTEXT *CertContext;
if (SelectIndex != 0xFFFF) {
if (OutputFileName == NULL) {
/// Delete certificate and update variable.
DeleteCertificateFromCertStore (CallbackData, SelectIndex);
} else {
/// Export certificate
/// Not supported yet.
return EFI_UNSUPPORTED;
}
}
DataSize = 0;
Data = NULL;
Status = GetCaCertificateVariable (&Data, &DataSize);
if (EFI_ERROR (Status)) {
/// If Certificate variable get failed, process need to update to empty.
/// So this section will do nothing.
}
InitializeListHead (&mFileListCertFileMenu.Head);
DEBUG ((DEBUG_INFO, "CA db total size : %d bytes\n", DataSize));
Index = 0;
ItemDataSize = DataSize;
TmpDataPtr = Data;
while (ItemDataSize > 0) {
Status = NextCertListInCertStore (TmpDataPtr, ItemDataSize, &SignatureType, &CertGuid, &CertListSize);
if (EFI_ERROR (Status)) {
break;
}
//
// Push ptr to next CertList.
//
CertPtr = TmpDataPtr;
VariableOffset = DataSize - ItemDataSize;
TmpDataPtr = TmpDataPtr + CertListSize;
ItemDataSize = ItemDataSize - CertListSize;
if (!CompareGuid (SignatureType, &gEfiCertX509Guid)) {
//
// The signature type is not supported in current implementation.
//
DEBUG ((DEBUG_INFO, "CERT is not supported\n"));
continue;
}
DEBUG ((DEBUG_INFO, "\nIndex %d: %g\n", Index, CertGuid));
DEBUG ((DEBUG_INFO, "GUID for CERT Offset:%08x Size:%d bytes\n", VariableOffset, CertListSize));
//
// Generate HII item
//
NewMenuEntry = CreateVariableMenuEntry ();
if (NewMenuEntry == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
//
// Check the validation of this certificate.
//
Status = DumpCertificate ((EFI_SIGNATURE_LIST *)CertPtr, NewMenuEntry);
if (EFI_ERROR (Status)) {
continue;
}
CertContext = (EM_CERTIFICATE_CONTEXT *) NewMenuEntry->VariableContext;
CertContext->VariableOffset = VariableOffset;
CertContext->CertLength = CertListSize;
CertContext->VariableLengthToEnd = DataSize - (VariableOffset + CertListSize);
InsertTailList (&mFileListCertFileMenu.Head, &NewMenuEntry->Link);
NewMenuEntry->OptionNumber = Index;
Index++;
}
mFileListCertFileMenu.MenuNumber = Index;
ON_EXIT:
if (Data != NULL) {
FreePool (Data);
}
return Status;
}
/**
Get the file format of enroll menu based on secure boot manager state.
@param[in] CallbackData Pointer to the secure boot manager private data
@param[out] FileFormat Returns the file format
@retval EFI_SUCCESS Success find the file system
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory.
@retval EFI_ABORTED User aborts the question dialog.
@retval EFI_NOT_FOUND File format is not found.
**/
STATIC
EFI_STATUS
GetEnrollMenuFileFormat (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
OUT UINTN *FileFormat
)
{
if (CallbackData == NULL || FileFormat == NULL) {
return EFI_INVALID_PARAMETER;
}
switch (CallbackData->FileListState) {
case TlsForm:
*FileFormat = EM_FILE_FORMAT_PEM;
break;
case VariableForm:
*FileFormat = EM_FILE_FORMAT_DEL_VAR;
break;
default:
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
/**
Enroll file based on the input file context.
@param[in] CallbackData Pointer to the secure boot manager private data
@param[in] MenuEntry Pointer to the enroll menu data
@param[in] FileContext Pointer to the file context
@retval EFI_SUCCESS Enroll file successfully.
@retval EFI_UNSUPPORTED Fail to read file.
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory.
@retval EFI_ABORTED User aborts the enroll process.
@retval Other Fail to enroll file.
**/
STATIC
EFI_STATUS
ProcessFile (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData,
IN ENROLL_MENU_ENTRY *MenuEntry,
IN EM_FILE_CONTEXT *FileContext
)
{
EFI_STATUS Status;
UINTN BufferSize;
UINT8 *Buffer;
UINTN DerSize;
UINT8 *DerData;
Status = ReadFile (FileContext, &BufferSize, &Buffer);
if (EFI_ERROR (Status) || Buffer == NULL) {
return EFI_UNSUPPORTED;
}
DEBUG ((DEBUG_INFO, "File size %d\n", BufferSize));
DerSize = 0;
DerData = NULL;
Status = TranslatePemToDer (Buffer, BufferSize, &DerData, &DerSize);
if (!EFI_ERROR (Status)) {
//
// Replace PEM data to DER, it can filter out unnecessary data in PEM data.
//
DEBUG ((DEBUG_INFO, "Translated size %d\n", DerSize));
FreePool (Buffer);
Buffer = DerData;
BufferSize = DerSize;
}
if (CallbackData->EmFileFormat == EM_FILE_FORMAT_PEM) {
Status = ProcessPemFile (CallbackData, BufferSize, Buffer);
}
FreePool (Buffer);
return Status;
}
/**
Initialize file system
@param[in] CallbackData Pointer to SECURE_BOOT_MANAGER_CALLBACK_DATA instance
@retval EFI_SUCCESS Initialize enroll menu successful
@retval Other Fail to get the file format of enroll menu.
**/
EFI_STATUS
InitializeFileListMenu (
IN TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData
)
{
EFI_STATUS Status;
Status = gBS->LocateProtocol (
&gCryptoServicesProtocolGuid,
NULL,
(VOID **) &mCryptoServices
);
if (EFI_ERROR (Status)) {
mCryptoServices = NULL;
}
if (CallbackData->H2ODialog == NULL) {
Status = gBS->LocateProtocol (
&gH2ODialogProtocolGuid,
NULL,
(VOID **) &CallbackData->H2ODialog
);
if (EFI_ERROR(Status)) {
return Status;
}
}
DEBUG ((DEBUG_INFO, "InitializeFileListMenu\n"));
InitializeListHead (&mFileListFsOptionMenu.Head);
InitializeListHead (&mFileListDirectoryMenu.Head);
if ((CallbackData->EmFileFormat == EM_FILE_FORMAT_DEL_VAR) || (CallbackData->EmFileFormat == EM_FILE_FORMAT_EXP_VAR)) {
DEBUG ((DEBUG_INFO, "InitializeCertificateListMenu\n"));
CallbackData->FileListState = VariableForm;
UpdateEmCertFileExplorer (CallbackData, 0);
} else if (CallbackData->EmFileFormat == EM_FILE_FORMAT_PEM) {
DEBUG ((DEBUG_INFO, "InitializeFileListMenu\n"));
CallbackData->EmDisplayContext = EM_UNKNOWN_CONTEXT;
CallbackData->FileListState = TlsForm;
UpdateEmFileExplorer (CallbackData, 0);
}
return EFI_SUCCESS;
}
/**
This function allows the caller to request the current
configuration for one or more named elements. The resulting
string is in <ConfigAltResp> format. Also, any and all alternative
configuration strings shall be appended to the end of the
current configuration string. If they are, they must appear
after the current configuration. They must contain the same
routing (GUID, NAME, PATH) as the current configuration string.
They must have an additional description indicating the type of
alternative configuration the string represents,
"ALTCFG=<StringToken>". That <StringToken> (when
converted from Hex UNICODE to binary) is a reference to a
string in the associated string pack.
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param[in] Request A null-terminated Unicode string in
<ConfigRequest> format. Note that this
includes the routing information as well as
the configurable name / value pairs. It is
invalid for this string to be in
<MultiConfigRequest> format.
@param[out] Progress On return, points to a character in the
Request string. Points to the string's null
terminator if request was successful. Points
to the most recent "&" before the first
failing name / value pair (or the beginning
of the string if the failure is in the first
name / value pair) if the request was not successful.
@param[out] Results A null-terminated Unicode string in
<ConfigAltResp> format which has all values
filled in for the names in the Request string.
String to be allocated by the called function.
@retval EFI_SUCCESS The Results string is filled with the
values corresponding to all requested
names.
@retval EFI_INVALID_PARAMETER For example, passing in a NULL
for the Request parameter
would result in this type of
error. In this case, the
Progress parameter would be
set to NULL.
@retval EFI_NOT_FOUND Routing data doesn't match any
known driver. Progress set to the
first character in the routing header.
Note: There is no requirement that the
driver validate the routing data. It
must skip the <ConfigHdr> in order to
process the names.
@retval EFI_INVALID_PARAMETER Illegal syntax. Progress set
to most recent "&" before the
error or the beginning of the
string.
@retval EFI_INVALID_PARAMETER Unknown name. Progress points
to the & before the name in
question.
**/
EFI_STATUS
EFIAPI
TlsCertificateManagementExtractConfig (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN CONST EFI_STRING Request,
OUT EFI_STRING *Progress,
OUT EFI_STRING *Results
)
{
if (Progress == NULL || Results == NULL) {
return EFI_INVALID_PARAMETER;
}
*Progress = Request;
return EFI_NOT_FOUND;
}
/**
This function applies changes in a driver's configuration.
Input is a Configuration, which has the routing data for this
driver followed by name / value configuration pairs. The driver
must apply those pairs to its configurable storage. If the
driver's configuration is stored in a linear block of data
and the driver's name / value pairs are in <BlockConfig>
format, it may use the ConfigToBlock helper function (above) to
simplify the job.
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param[in] Configuration A null-terminated Unicode string in
<ConfigString> format.
@param[out] Progress A pointer to a string filled in with the
offset of the most recent '&' before the
first failing name / value pair (or the
beginning of the string if the failure
is in the first name / value pair) or
the terminating NULL if all was
successful.
@retval EFI_SUCCESS The results have been distributed or are
awaiting distribution.
@retval EFI_INVALID_PARAMETER Passing in a NULL for the
Results parameter would result
in this type of error.
@retval EFI_NOT_FOUND Target for the specified routing data
was not found.
**/
EFI_STATUS
EFIAPI
TlsCertificateManagementRouteConfig (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN CONST EFI_STRING Configuration,
OUT EFI_STRING *Progress
)
{
// EFI_STATUS Status;
TLS_CERT_MANAGER_FORM_CALLBACK_INFO *CallbackInfo;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Progress == NULL) {
return EFI_INVALID_PARAMETER;
}
*Progress = Configuration;
if (Configuration == NULL) {
return EFI_INVALID_PARAMETER;
}
CallbackInfo = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This);
//
// Check routing data in <ConfigHdr>.
// Note: there is no name for Name/Value storage, only GUID will be checked
//
if (!HiiIsConfigHdrMatch (Configuration, CallbackInfo->FormGuid, mTlcAuthManageStorageName)) {
return EFI_NOT_FOUND;
}
// //
// // Get NV configuration data.
// //
// BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);
// ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);
// Status = gHiiConfigRouting->ConfigToBlock (
// gHiiConfigRouting,
// Configuration,
// (UINT8 *) &CallbackInfo->HttpBootNvData,
// &BufferSize,
// Progress
// );
// if (EFI_ERROR (Status)) {
// return Status;
// }
return EFI_SUCCESS;
}
/**
This function is called to provide results data to the driver.
This data consists of a unique key that is used to identify
which data is either being passed back or being asked for.
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param[in] Action Specifies the type of action taken by the browser.
@param[in] QuestionId A unique value which is sent to the original
exporting driver so that it can identify the type
of data to expect. The format of the data tends to
vary based on the opcode that generated the callback.
@param[in] Type The type of value for the question.
@param[in, out] Value A pointer to the data being sent to the original
exporting driver.
@param[out] ActionRequest On return, points to the action requested by the
callback function.
@retval EFI_SUCCESS The callback successfully handled the action.
@retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
variable and its data.
@retval EFI_DEVICE_ERROR The variable could not be saved.
@retval EFI_UNSUPPORTED The specified Action is not supported by the
callback.
**/
EFI_STATUS
EFIAPI
TlsCertificateManagementCallback (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN EFI_BROWSER_ACTION Action,
IN EFI_QUESTION_ID QuestionId,
IN UINT8 Type,
IN OUT EFI_IFR_TYPE_VALUE *Value,
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
)
{
EFI_STATUS Status;
TLS_CERT_MANAGER_FORM_CALLBACK_INFO *CallbackInfo;
TLS_CERT_MANAGER_CALLBACK_DATA *CallbackData;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_SUCCESS;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
CallbackInfo = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This);
CallbackData = &CallbackInfo->CallbackData;
//
// This page only have goto operation, so it will not get the callback of CHANGED.
//
// if (!mgH2OFormBrowserProtocoSupported && Action == EFI_BROWSER_ACTION_CHANGING) {
// //
// // For compatible to old form browser which only use EFI_BROWSER_ACTION_CHANGING action,
// // change action to EFI_BROWSER_ACTION_CHANGED to make it workable.
// //
// Action = EFI_BROWSER_ACTION_CHANGED;
// }
if (Action != EFI_BROWSER_ACTION_CHANGING) {
return EFI_UNSUPPORTED;
}
DEBUG ((DEBUG_INFO, "Callback Action: %d QuestionId: %08x\n", Action, QuestionId));
if (Value == NULL) {
return EFI_INVALID_PARAMETER;
}
switch (QuestionId) {
case KEY_IMPORT_CERT_FILE:
case KEY_DELETE_CERT_FILE:
case KEY_EXPORT_CERT_FILE:
CallbackData->EmFileFormat = 0;
if (QuestionId == KEY_IMPORT_CERT_FILE) {
CallbackData->EmFileFormat = EM_FILE_FORMAT_PEM;
} else if (QuestionId == KEY_DELETE_CERT_FILE) {
CallbackData->EmFileFormat = EM_FILE_FORMAT_DEL_VAR;
} else if (QuestionId == KEY_EXPORT_CERT_FILE) {
CallbackData->EmFileFormat = EM_FILE_FORMAT_EXP_VAR;
}
CallbackData->RegisteredHandle = CallbackInfo->RegisteredHandle;
CallbackData->EmDisplayContext = 0;
CallbackData->Type = Type;
CallbackData->Value = Value;
Status = InitializeFileListMenu (CallbackData);
break;
default:
DEBUG ((DEBUG_INFO, "Callback Action: %d QuestionId: %08x DEFAULT\n", Action, QuestionId));
if (QuestionId >= ENROLL_FILE_OPTION_OFFSET) {
if (CallbackData->FileListState == TlsForm) {
if (UpdateEmFileExplorer (CallbackData, QuestionId)) {
CallbackData->EmDisplayContext = EM_UNKNOWN_CONTEXT;
UpdateEmFileExplorer (CallbackData, 0);
}
} else if (CallbackData->FileListState == VariableForm) {
UpdateEmCertFileExplorer (CallbackData, QuestionId);
}
break;
}
};
return Status;
}
/**
Initialize the configuration form to SCU Security\Link.
@param[in] Private Pointer to the driver private data.
@retval EFI_SUCCESS The configuration form is initialized.
@retval EFI_INVALID_PARAMETER HiiHandle is NULL.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
EFI_STATUS
TlsCertificateManagementFormUnload (
IN VOID *CallbackInfo
)
{
TLS_CERT_MANAGER_FORM_CALLBACK_INFO *TmpCallbackInfo;
TmpCallbackInfo = CallbackInfo;
//
// Uninstall Device Path Protocol and Config Access protocol to driver handle.
//
gBS->UninstallMultipleProtocolInterfaces (
TmpCallbackInfo->ChildHandle,
&gEfiDevicePathProtocolGuid,
TmpCallbackInfo->HiiVendorDevicePath,
&gEfiHiiConfigAccessProtocolGuid,
&TmpCallbackInfo->ConfigAccess,
NULL
);
HiiRemovePackages (TmpCallbackInfo->ChildHandle);
if (TmpCallbackInfo->HiiVendorDevicePath != NULL) {
FreePool (TmpCallbackInfo->HiiVendorDevicePath);
}
if (TmpCallbackInfo != NULL) {
FreePool (TmpCallbackInfo);
}
if (mSecureBootFileOptionStrDepository != NULL) {
FreePool (mSecureBootFileOptionStrDepository);
}
return EFI_SUCCESS;
}
/**
Initialize the configuration form to SCU Security\Link.
@param[in] Private Pointer to the driver private data.
@retval EFI_SUCCESS The configuration form is initialized.
@retval EFI_INVALID_PARAMETER HiiHandle is NULL.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
**/
EFI_STATUS
TlsCertificateManagementFormInit (
IN VOID **CallbackInfo
)
{
EFI_STATUS Status;
VENDOR_DEVICE_PATH *VendorDeviceNode;
UINTN SizeOfBuffer;
TLS_CERT_MANAGER_FORM_CALLBACK_INFO *TmpCallbackInfo;
mSecureBootFileOptionStrDepository = AllocateZeroPool (sizeof (STRING_DEPOSITORY));
TmpCallbackInfo = AllocateZeroPool (sizeof (TLS_CERT_MANAGER_FORM_CALLBACK_INFO));
VendorDeviceNode = AllocateZeroPool (sizeof (VENDOR_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL));
if ((TmpCallbackInfo == NULL) || (VendorDeviceNode == NULL) || (mSecureBootFileOptionStrDepository == NULL)) {
if (TmpCallbackInfo != NULL) {
FreePool (TmpCallbackInfo);
}
if (VendorDeviceNode != NULL) {
FreePool (VendorDeviceNode);
}
if (mSecureBootFileOptionStrDepository != NULL) {
FreePool (mSecureBootFileOptionStrDepository);
}
return EFI_OUT_OF_RESOURCES;
}
//
// Set VENDOR device path content.
//
VendorDeviceNode->Header.Type = HARDWARE_DEVICE_PATH;
VendorDeviceNode->Header.SubType = HW_VENDOR_DP;
CopyGuid (&VendorDeviceNode->Guid, &gEfiCallerIdGuid);
SetDevicePathNodeLength (VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));
//
// Set DevicePath end node.
//
SetDevicePathEndNode (VendorDeviceNode + 1);
TmpCallbackInfo->Signature = TLS_AUTH_CONFIG_PRIVATE_DATA_SIGNATURE;
TmpCallbackInfo->HiiVendorDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) VendorDeviceNode;
TmpCallbackInfo->ConfigAccess.ExtractConfig = TlsCertificateManagementExtractConfig;
TmpCallbackInfo->ConfigAccess.RouteConfig = TlsCertificateManagementRouteConfig;
TmpCallbackInfo->ConfigAccess.Callback = TlsCertificateManagementCallback;
TmpCallbackInfo->FormGuid = &gTlsAuthConfigGuid;
//
// Install Device Path Protocol and Config Access protocol to driver handle.
//
Status = gBS->InstallMultipleProtocolInterfaces (
&TmpCallbackInfo->ChildHandle,
&gEfiDevicePathProtocolGuid,
TmpCallbackInfo->HiiVendorDevicePath,
&gEfiHiiConfigAccessProtocolGuid,
&TmpCallbackInfo->ConfigAccess,
NULL
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Publish our HII data.
//
TmpCallbackInfo->RegisteredHandle = HiiAddPackages (
&gTlsAuthConfigGuid,
TmpCallbackInfo->ChildHandle,
TlsDxeStrings,
TlsAuthConfigVfrBin,
NULL
);
if (TmpCallbackInfo->RegisteredHandle == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
SizeOfBuffer = sizeof (EFI_GUID);
Status = LibPcdSetExPtrS (&gLinkSecurityTokenSpaceGuid, PcdToken (PcdH2OTlsCertManageFormsetGuid), &SizeOfBuffer, &gTlsAuthConfigGuid);
ASSERT_EFI_ERROR (Status);
*CallbackInfo = TmpCallbackInfo;
return EFI_SUCCESS;
Error:
TlsCertificateManagementFormUnload (TmpCallbackInfo);
return Status;
}