457 lines
12 KiB
C

/** @file
Copyright (C) 2019, vit9696. All rights reserved.
All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "BootManagementInternal.h"
#include <Guid/FileInfo.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h>
#include <Library/FileHandleLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcAppleKeysLib.h>
#include <Library/OcFileLib.h>
#include <Library/OcStringLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
STATIC
EFI_DEVICE_PATH_PROTOCOL *
InternalGetFirstDeviceBootFilePath (
IN APPLE_BOOT_POLICY_PROTOCOL *BootPolicy,
IN CONST EFI_DEVICE_PATH_PROTOCOL *DmgDevicePath,
IN UINTN DmgDevicePathSize
)
{
EFI_DEVICE_PATH_PROTOCOL *BootDevicePath;
EFI_STATUS Status;
INTN CmpResult;
CONST EFI_DEVICE_PATH_PROTOCOL *FsDevicePath;
UINTN FsDevicePathSize;
UINTN NumHandles;
EFI_HANDLE *HandleBuffer;
UINTN Index;
ASSERT (BootPolicy != NULL);
ASSERT (DmgDevicePath != NULL);
ASSERT (DmgDevicePathSize >= END_DEVICE_PATH_LENGTH);
BootDevicePath = NULL;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return NULL;
}
for (Index = 0; Index < NumHandles; ++Index) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiDevicePathProtocolGuid,
(VOID **)&FsDevicePath
);
if (EFI_ERROR (Status)) {
continue;
}
FsDevicePathSize = GetDevicePathSize (FsDevicePath);
if (FsDevicePathSize < DmgDevicePathSize) {
continue;
}
CmpResult = CompareMem (
FsDevicePath,
DmgDevicePath,
(DmgDevicePathSize - END_DEVICE_PATH_LENGTH)
);
if (CmpResult != 0) {
continue;
}
Status = BootPolicy->GetBootFileEx (
HandleBuffer[Index],
BootPolicyOk,
&BootDevicePath
);
if (!EFI_ERROR (Status)) {
break;
}
BootDevicePath = NULL;
}
FreePool (HandleBuffer);
return BootDevicePath;
}
STATIC
EFI_DEVICE_PATH_PROTOCOL *
InternalGetDiskImageBootFile (
OUT INTERNAL_DMG_LOAD_CONTEXT *Context,
IN APPLE_BOOT_POLICY_PROTOCOL *BootPolicy,
IN UINT32 Policy,
IN UINTN DmgFileSize,
IN VOID *ChunklistBuffer OPTIONAL,
IN UINT32 ChunklistBufferSize OPTIONAL
)
{
EFI_DEVICE_PATH_PROTOCOL *DevPath;
BOOLEAN Result;
OC_APPLE_CHUNKLIST_CONTEXT ChunklistContext;
CONST EFI_DEVICE_PATH_PROTOCOL *DmgDevicePath;
UINTN DmgDevicePathSize;
ASSERT (Context != NULL);
ASSERT (BootPolicy != NULL);
ASSERT (DmgFileSize > 0);
if (ChunklistBuffer == NULL) {
if ((Policy & OC_LOAD_REQUIRE_APPLE_SIGN) != 0) {
return NULL;
}
} else if ((Policy & (OC_LOAD_VERIFY_APPLE_SIGN | OC_LOAD_REQUIRE_TRUSTED_KEY)) != 0) {
ASSERT (ChunklistBufferSize > 0);
Result = OcAppleChunklistInitializeContext (
&ChunklistContext,
ChunklistBuffer,
ChunklistBufferSize
);
if (!Result) {
return NULL;
}
if ((Policy & OC_LOAD_REQUIRE_TRUSTED_KEY) != 0) {
Result = FALSE;
//
// FIXME: Properly abstract OcAppleKeysLib.
//
if ((Policy & OC_LOAD_TRUST_APPLE_V1_KEY) != 0) {
Result = OcAppleChunklistVerifySignature (
&ChunklistContext,
(RSA_PUBLIC_KEY *)&PkDataBase[0].PublicKey
);
}
if (!Result && ((Policy & OC_LOAD_TRUST_APPLE_V2_KEY) != 0)) {
Result = OcAppleChunklistVerifySignature (
&ChunklistContext,
(RSA_PUBLIC_KEY *)&PkDataBase[1].PublicKey
);
}
if (!Result) {
return NULL;
}
}
Result = OcAppleDiskImageVerifyData (
Context->DmgContext,
&ChunklistContext
);
if (!Result) {
//
// FIXME: Warn user instead of aborting when OC_LOAD_REQUIRE_TRUSTED_KEY
// is not set.
//
return NULL;
}
}
Context->BlockIoHandle = OcAppleDiskImageInstallBlockIo (
Context->DmgContext,
DmgFileSize,
&DmgDevicePath,
&DmgDevicePathSize
);
if (Context->BlockIoHandle == NULL) {
return NULL;
}
DevPath = InternalGetFirstDeviceBootFilePath (
BootPolicy,
DmgDevicePath,
DmgDevicePathSize
);
if (DevPath == NULL) {
OcAppleDiskImageUninstallBlockIo (
Context->DmgContext,
Context->BlockIoHandle
);
return NULL;
}
return DevPath;
}
STATIC
EFI_FILE_INFO *
InternalFindFirstDmgFileName (
IN EFI_FILE_PROTOCOL *Directory,
OUT UINTN *FileNameLen
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
BOOLEAN NoFile;
UINTN ExtOffset;
INTN Result;
ASSERT (Directory != NULL);
for (
Status = FileHandleFindFirstFile (Directory, &FileInfo), NoFile = FALSE;
(!EFI_ERROR (Status) && !NoFile);
Status = FileHandleFindNextFile (Directory, FileInfo, &NoFile)
) {
if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
continue;
}
ExtOffset = (StrLen (FileInfo->FileName) - L_STR_LEN (L".dmg"));
Result = StrCmp (&FileInfo->FileName[ExtOffset], L".dmg");
if (Result == 0) {
if (FileNameLen != NULL) {
*FileNameLen = ExtOffset;
}
return FileInfo;
}
}
return NULL;
}
STATIC
EFI_FILE_INFO *
InternalFindDmgChunklist (
IN EFI_FILE_PROTOCOL *Directory,
IN CONST CHAR16 *DmgFileName,
IN UINTN DmgFileNameLen
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
BOOLEAN NoFile;
UINTN NameLen;
INTN Result;
UINTN ChunklistFileNameLen;
ChunklistFileNameLen = (DmgFileNameLen + L_STR_LEN (".chunklist"));
for (
Status = FileHandleFindFirstFile (Directory, &FileInfo), NoFile = FALSE;
(!EFI_ERROR (Status) && !NoFile);
Status = FileHandleFindNextFile (Directory, FileInfo, &NoFile)
) {
if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
continue;
}
NameLen = StrLen (FileInfo->FileName);
if (NameLen != ChunklistFileNameLen) {
continue;
}
Result = StrnCmp (FileInfo->FileName, DmgFileName, DmgFileNameLen);
if (Result != 0) {
continue;
}
Result = StrCmp (&FileInfo->FileName[DmgFileNameLen], L".chunklist");
if (Result == 0) {
return FileInfo;
}
}
return NULL;
}
EFI_DEVICE_PATH_PROTOCOL *
InternalLoadDmg (
IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context,
IN APPLE_BOOT_POLICY_PROTOCOL *BootPolicy,
IN UINT32 Policy
)
{
EFI_DEVICE_PATH_PROTOCOL *DevPath;
EFI_STATUS Status;
BOOLEAN Result;
EFI_FILE_PROTOCOL *DmgDir;
UINTN DmgFileNameLen;
EFI_FILE_INFO *DmgFileInfo;
EFI_FILE_PROTOCOL *DmgFile;
UINT32 DmgFileSize;
EFI_FILE_INFO *ChunklistFileInfo;
EFI_FILE_PROTOCOL *ChunklistFile;
UINT32 ChunklistFileSize;
VOID *ChunklistBuffer;
ASSERT (Context != NULL);
ASSERT (BootPolicy != NULL);
Status = OcOpenFileByDevicePath (
&Context->DevicePath,
&DmgDir,
EFI_FILE_MODE_READ,
EFI_FILE_DIRECTORY
);
if (EFI_ERROR (Status)) {
return NULL;
}
DmgFileInfo = InternalFindFirstDmgFileName (DmgDir, &DmgFileNameLen);
if (DmgFileInfo == NULL) {
DmgDir->Close (DmgDir);
return NULL;
}
Status = DmgDir->Open (
DmgDir,
&DmgFile,
DmgFileInfo->FileName,
EFI_FILE_MODE_READ,
0
);
if (EFI_ERROR (Status)) {
FreePool (DmgFileInfo);
DmgDir->Close (DmgDir);
return NULL;
}
Status = GetFileSize (DmgFile, &DmgFileSize);
if (EFI_ERROR (Status)) {
FreePool (DmgFileInfo);
DmgDir->Close (DmgDir);
DmgFile->Close (DmgFile);
return NULL;
}
Context->DmgContext = AllocatePool (sizeof (*Context->DmgContext));
if (Context->DmgContext == NULL) {
return NULL;
}
Result = OcAppleDiskImageInitializeFromFile (Context->DmgContext, DmgFile);
DmgFile->Close (DmgFile);
if (!Result) {
FreePool (DmgFileInfo);
FreePool (Context->DmgContext);
DmgDir->Close (DmgDir);
return NULL;
}
ChunklistBuffer = NULL;
ChunklistFileSize = 0;
ChunklistFileInfo = InternalFindDmgChunklist (
DmgDir,
DmgFileInfo->FileName,
DmgFileNameLen
);
if (ChunklistFileInfo != NULL) {
Status = DmgDir->Open (
DmgDir,
&ChunklistFile,
ChunklistFileInfo->FileName,
EFI_FILE_MODE_READ,
0
);
if (!EFI_ERROR (Status)) {
Status = GetFileSize (ChunklistFile, &ChunklistFileSize);
if (Status == EFI_SUCCESS) {
ChunklistBuffer = AllocatePool (ChunklistFileSize);
if (ChunklistBuffer == NULL) {
ChunklistFileSize = 0;
} else {
Status = GetFileData (ChunklistFile, 0, ChunklistFileSize, ChunklistBuffer);
if (EFI_ERROR (Status)) {
FreePool (ChunklistBuffer);
ChunklistBuffer = NULL;
ChunklistFileSize = 0;
}
}
}
ChunklistFile->Close (ChunklistFile);
}
FreePool (ChunklistFileInfo);
}
FreePool (DmgFileInfo);
DmgDir->Close (DmgDir);
DevPath = InternalGetDiskImageBootFile (
Context,
BootPolicy,
Policy,
DmgFileSize,
ChunklistBuffer,
ChunklistFileSize
);
Context->DevicePath = DevPath;
if (DevPath == NULL) {
OcAppleDiskImageFreeFile (Context->DmgContext);
FreePool (Context->DmgContext);
}
if (ChunklistBuffer != NULL) {
FreePool (ChunklistBuffer);
}
return DevPath;
}
VOID
InternalUnloadDmg (
IN INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext
)
{
if (DmgLoadContext->DevicePath != NULL) {
FreePool (DmgLoadContext->DevicePath);
OcAppleDiskImageUninstallBlockIo (
DmgLoadContext->DmgContext,
DmgLoadContext->BlockIoHandle
);
OcAppleDiskImageFreeContext (DmgLoadContext->DmgContext);
FreePool (DmgLoadContext->DmgContext);
DmgLoadContext->DevicePath = NULL;
}
}