OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
DmgBootSupport.c
Go to the documentation of this file.
1
16
17#include <Guid/FileInfo.h>
18
20#include <Library/BaseMemoryLib.h>
21#include <Library/DebugLib.h>
22#include <Library/DevicePathLib.h>
23#include <Library/FileHandleLib.h>
24#include <Library/MemoryAllocationLib.h>
26#include <Library/OcFileLib.h>
27#include <Library/OcStringLib.h>
28#include <Library/UefiBootServicesTableLib.h>
29#include <Library/UefiLib.h>
30#include <Library/UefiRuntimeServicesTableLib.h>
31
32STATIC
33EFI_DEVICE_PATH_PROTOCOL *
35 IN CONST EFI_DEVICE_PATH_PROTOCOL *DmgDevicePath,
36 IN UINTN DmgDevicePathSize
37 )
38{
39 EFI_DEVICE_PATH_PROTOCOL *BootDevicePath;
40
41 EFI_STATUS Status;
42 INTN CmpResult;
43
44 CONST EFI_DEVICE_PATH_PROTOCOL *FsDevicePath;
45 UINTN FsDevicePathSize;
46
47 UINTN NumHandles;
48 EFI_HANDLE *HandleBuffer;
49 UINTN Index;
50
51 ASSERT (DmgDevicePath != NULL);
52 ASSERT (DmgDevicePathSize >= END_DEVICE_PATH_LENGTH);
53
54 BootDevicePath = NULL;
55
56 Status = gBS->LocateHandleBuffer (
57 ByProtocol,
59 NULL,
60 &NumHandles,
61 &HandleBuffer
62 );
63 if (EFI_ERROR (Status)) {
64 return NULL;
65 }
66
67 for (Index = 0; Index < NumHandles; ++Index) {
68 Status = gBS->HandleProtocol (
69 HandleBuffer[Index],
71 (VOID **)&FsDevicePath
72 );
73 if (EFI_ERROR (Status)) {
74 continue;
75 }
76
77 FsDevicePathSize = GetDevicePathSize (FsDevicePath);
78
79 if (FsDevicePathSize < DmgDevicePathSize) {
80 continue;
81 }
82
83 CmpResult = CompareMem (
84 FsDevicePath,
85 DmgDevicePath,
86 (DmgDevicePathSize - END_DEVICE_PATH_LENGTH)
87 );
88 if (CmpResult != 0) {
89 continue;
90 }
91
93 HandleBuffer[Index],
96 &BootDevicePath
97 );
98 if (!EFI_ERROR (Status)) {
99 break;
100 }
101
102 BootDevicePath = NULL;
103 }
104
105 FreePool (HandleBuffer);
106
107 return BootDevicePath;
108}
109
110STATIC
111EFI_DEVICE_PATH_PROTOCOL *
113 OUT INTERNAL_DMG_LOAD_CONTEXT *Context,
114 IN OC_DMG_LOADING_SUPPORT DmgLoading,
115 IN UINTN DmgFileSize,
116 IN VOID *ChunklistBuffer OPTIONAL,
117 IN UINT32 ChunklistBufferSize OPTIONAL
118 )
119{
120 EFI_DEVICE_PATH_PROTOCOL *DevPath;
121
122 BOOLEAN Result;
123 OC_APPLE_CHUNKLIST_CONTEXT ChunklistContext;
124
125 CONST EFI_DEVICE_PATH_PROTOCOL *DmgDevicePath;
126 UINTN DmgDevicePathSize;
127
128 ASSERT (Context != NULL);
129 ASSERT (DmgFileSize > 0);
130
131 if (DmgLoading == OcDmgLoadingAppleSigned) {
132 if (ChunklistBuffer == NULL) {
133 DEBUG ((DEBUG_WARN, "OCB: Missing DMG signature, aborting\n"));
134 return NULL;
135 }
136
137 ASSERT (ChunklistBufferSize > 0);
138
140 &ChunklistContext,
141 ChunklistBuffer,
142 ChunklistBufferSize
143 );
144 if (!Result) {
145 DEBUG ((
146 DEBUG_INFO,
147 "OCB: Failed to initialise DMG Chunklist context\n"
148 ));
149 return NULL;
150 }
151
152 //
153 // FIXME: Properly abstract OcAppleKeysLib.
154 //
156 &ChunklistContext,
157 PkDataBase[0].PublicKey
158 );
159
160 if (!Result) {
162 &ChunklistContext,
163 PkDataBase[1].PublicKey
164 );
165 }
166
167 if (!Result) {
168 DEBUG ((DEBUG_WARN, "OCB: DMG is not trusted, aborting\n"));
169 return NULL;
170 }
171
173 Context->DmgContext,
174 &ChunklistContext
175 );
176 if (!Result) {
177 DEBUG ((DEBUG_WARN, "OCB: DMG has been altered\n"));
178 return NULL;
179 }
180 }
181
182 Context->BlockIoHandle = OcAppleDiskImageInstallBlockIo (
183 Context->DmgContext,
184 DmgFileSize,
185 &DmgDevicePath,
186 &DmgDevicePathSize
187 );
188 if (Context->BlockIoHandle == NULL) {
189 DEBUG ((DEBUG_INFO, "OCB: Failed to install DMG Block I/O\n"));
190 return NULL;
191 }
192
194 DmgDevicePath,
195 DmgDevicePathSize
196 );
197 if (DevPath == NULL) {
198 DEBUG ((DEBUG_INFO, "OCB: Failed to get bootable file off DMG\n"));
199
201 Context->DmgContext,
202 Context->BlockIoHandle
203 );
204 return NULL;
205 }
206
207 return DevPath;
208}
209
210STATIC
211EFI_FILE_INFO *
213 IN EFI_FILE_PROTOCOL *Directory,
214 OUT UINTN *FileNameLen
215 )
216{
217 EFI_STATUS Status;
218 EFI_FILE_INFO *FileInfo;
219 BOOLEAN NoFile;
220 UINTN ExtOffset;
221 UINTN Length;
222 INTN Result;
223
224 ASSERT (Directory != NULL);
225
226 for (
227 Status = FileHandleFindFirstFile (Directory, &FileInfo), NoFile = FALSE;
228 (!EFI_ERROR (Status) && !NoFile);
229 Status = FileHandleFindNextFile (Directory, FileInfo, &NoFile)
230 )
231 {
232 if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
233 continue;
234 }
235
236 //
237 // Discard filenames that do not contain characters prior to .dmg extension.
238 //
239 Length = StrLen (FileInfo->FileName);
240 if (Length <= L_STR_LEN (L".dmg")) {
241 continue;
242 }
243
244 ExtOffset = Length - L_STR_LEN (L".dmg");
245 Result = StrCmp (&FileInfo->FileName[ExtOffset], L".dmg");
246 if (Result == 0) {
247 if (FileNameLen != NULL) {
248 *FileNameLen = ExtOffset;
249 }
250
251 return FileInfo;
252 }
253 }
254
255 return NULL;
256}
257
258STATIC
259EFI_FILE_INFO *
261 IN EFI_FILE_PROTOCOL *Directory,
262 IN CONST CHAR16 *DmgFileName,
263 IN UINTN DmgFileNameLen
264 )
265{
266 EFI_STATUS Status;
267 EFI_FILE_INFO *FileInfo;
268 BOOLEAN NoFile;
269 UINTN NameLen;
270 INTN Result;
271 UINTN ChunklistFileNameLen;
272
273 ChunklistFileNameLen = (DmgFileNameLen + L_STR_LEN (".chunklist"));
274
275 for (
276 Status = FileHandleFindFirstFile (Directory, &FileInfo), NoFile = FALSE;
277 (!EFI_ERROR (Status) && !NoFile);
278 Status = FileHandleFindNextFile (Directory, FileInfo, &NoFile)
279 )
280 {
281 if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
282 continue;
283 }
284
285 NameLen = StrLen (FileInfo->FileName);
286
287 if (NameLen != ChunklistFileNameLen) {
288 continue;
289 }
290
291 Result = StrnCmp (FileInfo->FileName, DmgFileName, DmgFileNameLen);
292 if (Result != 0) {
293 continue;
294 }
295
296 Result = StrCmp (&FileInfo->FileName[DmgFileNameLen], L".chunklist");
297 if (Result == 0) {
298 DEBUG ((
299 DEBUG_INFO,
300 "OCB: Found chunklist %s for DMG %s[%d]\n",
301 FileInfo->FileName,
302 DmgFileName,
303 DmgFileNameLen
304 ));
305 return FileInfo;
306 }
307 }
308
309 DEBUG ((
310 DEBUG_INFO,
311 "OCB: Found no chunklist for DMG %s[%d]\n",
312 DmgFileName,
313 DmgFileNameLen
314 ));
315 return NULL;
316}
317
318EFI_DEVICE_PATH_PROTOCOL *
320 IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context,
321 IN OC_DMG_LOADING_SUPPORT DmgLoading,
322 IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext
323 )
324{
325 EFI_DEVICE_PATH_PROTOCOL *DevPath;
326
327 EFI_STATUS Status;
328 BOOLEAN Result;
329
330 EFI_FILE_PROTOCOL *DmgDir;
331
332 UINTN DmgFileNameLen;
333 EFI_FILE_INFO *DmgFileInfo;
334 EFI_FILE_PROTOCOL *DmgFile;
335 UINT32 DmgFileSize;
336
337 EFI_FILE_INFO *ChunklistFileInfo;
338 EFI_FILE_PROTOCOL *ChunklistFile;
339 UINT32 ChunklistFileSize;
340 VOID *ChunklistBuffer;
341
342 CHAR16 *DevPathText;
343
344 ASSERT (Context != NULL);
345
346 if (DmgPreloadContext->DmgContext != NULL) {
347 Context->DmgContext = DmgPreloadContext->DmgContext;
348 DmgFileSize = DmgPreloadContext->DmgFileSize;
349 } else {
350 if (DmgPreloadContext->DmgFile != NULL) {
351 DmgFile = DmgPreloadContext->DmgFile;
352 DmgFileSize = DmgPreloadContext->DmgFileSize;
353 } else {
354 DevPath = Context->DevicePath;
355 Status = OcOpenFileByDevicePath (
356 &DevPath,
357 &DmgDir,
358 EFI_FILE_MODE_READ,
359 EFI_FILE_DIRECTORY
360 );
361 if (EFI_ERROR (Status)) {
362 DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE);
363 DEBUG ((DEBUG_INFO, "OCB: Failed to open DMG directory %s\n", DevPathText));
364 if (DevPathText != NULL) {
365 FreePool (DevPathText);
366 }
367
368 return NULL;
369 }
370
371 DmgFileInfo = InternalFindFirstDmgFileName (DmgDir, &DmgFileNameLen);
372 if (DmgFileInfo == NULL) {
373 DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE);
374 DEBUG ((DEBUG_INFO, "OCB: Unable to find any DMG at %s\n"));
375 if (DevPathText != NULL) {
376 FreePool (DevPathText);
377 }
378
379 DmgDir->Close (DmgDir);
380 return NULL;
381 }
382
383 Status = OcSafeFileOpen (
384 DmgDir,
385 &DmgFile,
386 DmgFileInfo->FileName,
387 EFI_FILE_MODE_READ,
388 0
389 );
390 if (EFI_ERROR (Status)) {
391 DEBUG ((
392 DEBUG_INFO,
393 "OCB: Failed to open DMG file %s - %r\n",
394 DmgFileInfo->FileName,
395 Status
396 ));
397
398 FreePool (DmgFileInfo);
399 DmgDir->Close (DmgDir);
400 return NULL;
401 }
402
403 Status = OcGetFileSize (DmgFile, &DmgFileSize);
404 if (EFI_ERROR (Status)) {
405 DEBUG ((
406 DEBUG_INFO,
407 "OCB: Failed to retrieve DMG file size - %r\n",
408 Status
409 ));
410
411 FreePool (DmgFileInfo);
412 DmgDir->Close (DmgDir);
413 DmgFile->Close (DmgFile);
414 return NULL;
415 }
416 }
417
418 Context->DmgContext = AllocatePool (sizeof (*Context->DmgContext));
419 if (Context->DmgContext == NULL) {
420 DEBUG ((DEBUG_INFO, "OCB: Failed to allocate DMG context\n"));
421 return NULL;
422 }
423
424 Result = OcAppleDiskImageInitializeFromFile (Context->DmgContext, DmgFile);
425
426 DmgFile->Close (DmgFile);
427
428 if (!Result) {
429 DEBUG ((DEBUG_INFO, "OCB: Failed to initialise DMG from file\n"));
430
431 if (DmgPreloadContext->DmgFile == NULL) {
432 FreePool (DmgFileInfo);
433 DmgDir->Close (DmgDir);
434 }
435
436 FreePool (Context->DmgContext);
437 return NULL;
438 }
439 }
440
441 ChunklistBuffer = NULL;
442 ChunklistFileSize = 0;
443 if ( (DmgPreloadContext->DmgFile != NULL)
444 || (DmgPreloadContext->DmgContext != NULL))
445 {
446 if (DmgPreloadContext->ChunklistBuffer != NULL) {
447 ChunklistBuffer = DmgPreloadContext->ChunklistBuffer;
448 ChunklistFileSize = DmgPreloadContext->ChunklistFileSize;
449 }
450 } else {
451 ChunklistFileInfo = InternalFindDmgChunklist (
452 DmgDir,
453 DmgFileInfo->FileName,
454 DmgFileNameLen
455 );
456 if (ChunklistFileInfo != NULL) {
457 Status = OcSafeFileOpen (
458 DmgDir,
459 &ChunklistFile,
460 ChunklistFileInfo->FileName,
461 EFI_FILE_MODE_READ,
462 0
463 );
464 if (!EFI_ERROR (Status)) {
465 Status = OcGetFileSize (ChunklistFile, &ChunklistFileSize);
466 if (Status == EFI_SUCCESS) {
467 ChunklistBuffer = AllocatePool (ChunklistFileSize);
468
469 if (ChunklistBuffer == NULL) {
470 ChunklistFileSize = 0;
471 } else {
472 Status = OcGetFileData (ChunklistFile, 0, ChunklistFileSize, ChunklistBuffer);
473 if (EFI_ERROR (Status)) {
474 FreePool (ChunklistBuffer);
475 ChunklistBuffer = NULL;
476 ChunklistFileSize = 0;
477 }
478 }
479 }
480
481 ChunklistFile->Close (ChunklistFile);
482 }
483
484 FreePool (ChunklistFileInfo);
485 }
486 }
487
488 if ( (DmgPreloadContext->DmgFile == NULL)
489 && (DmgPreloadContext->DmgContext == NULL))
490 {
491 FreePool (DmgFileInfo);
492 DmgDir->Close (DmgDir);
493 }
494
496 Context,
497 DmgLoading,
498 DmgFileSize,
499 ChunklistBuffer,
500 ChunklistFileSize
501 );
502 Context->DevicePath = DevPath;
503
504 if (DevPath != NULL) {
505 //
506 // If we succeeded, we need to disable Apple Secure Boot, as DMG images
507 // are currently only verified with the chunklist.
508 //
510 } else {
511 DEBUG ((DEBUG_INFO, "OCB: Failed to retrieve boot file from DMG\n"));
512
513 OcAppleDiskImageFreeFile (Context->DmgContext);
514 FreePool (Context->DmgContext);
515 }
516
517 if (ChunklistBuffer != NULL) {
518 FreePool (ChunklistBuffer);
519 }
520
521 return DevPath;
522}
523
524VOID
526 IN INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext
527 )
528{
529 if (DmgLoadContext->DevicePath != NULL) {
530 FreePool (DmgLoadContext->DevicePath);
532 DmgLoadContext->DmgContext,
533 DmgLoadContext->BlockIoHandle
534 );
535 OcAppleDiskImageFreeContext (DmgLoadContext->DmgContext);
536 FreePool (DmgLoadContext->DmgContext);
537 DmgLoadContext->DevicePath = NULL;
538 //
539 // This code should never execute, as with Apple Secure Boot
540 // it should always reboot on failure, but just in case.
541 //
543 }
544}
UINT64 Length
EFI_DEVICE_PATH_PROTOCOL * InternalLoadDmg(IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context, IN OC_DMG_LOADING_SUPPORT DmgLoading, IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext)
STATIC EFI_FILE_INFO * InternalFindFirstDmgFileName(IN EFI_FILE_PROTOCOL *Directory, OUT UINTN *FileNameLen)
STATIC EFI_DEVICE_PATH_PROTOCOL * InternalGetFirstDeviceBootFilePath(IN CONST EFI_DEVICE_PATH_PROTOCOL *DmgDevicePath, IN UINTN DmgDevicePathSize)
STATIC EFI_DEVICE_PATH_PROTOCOL * InternalGetDiskImageBootFile(OUT INTERNAL_DMG_LOAD_CONTEXT *Context, IN OC_DMG_LOADING_SUPPORT DmgLoading, IN UINTN DmgFileSize, IN VOID *ChunklistBuffer OPTIONAL, IN UINT32 ChunklistBufferSize OPTIONAL)
STATIC EFI_FILE_INFO * InternalFindDmgChunklist(IN EFI_FILE_PROTOCOL *Directory, IN CONST CHAR16 *DmgFileName, IN UINTN DmgFileNameLen)
VOID InternalUnloadDmg(IN INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext)
CONST CHAR16 * gAppleBootPolicyPredefinedPaths[]
CONST UINTN gAppleBootPolicyNumPredefinedPaths
EFI_STATUS OcBootPolicyGetBootFileEx(IN EFI_HANDLE Device, IN CONST CHAR16 **PredefinedPaths, IN UINTN NumPredefinedPaths, OUT EFI_DEVICE_PATH_PROTOCOL **FilePath)
BOOLEAN OcAppleChunklistVerifySignature(IN OUT OC_APPLE_CHUNKLIST_CONTEXT *Context, IN CONST OC_RSA_PUBLIC_KEY *PublicKey)
BOOLEAN OcAppleChunklistInitializeContext(OUT OC_APPLE_CHUNKLIST_CONTEXT *Context, IN OUT VOID *Buffer, IN UINT32 BufferSize)
BOOLEAN OcAppleDiskImageVerifyData(IN OUT OC_APPLE_DISK_IMAGE_CONTEXT *Context, IN OUT OC_APPLE_CHUNKLIST_CONTEXT *ChunklistContext)
VOID OcAppleDiskImageUninstallBlockIo(IN OC_APPLE_DISK_IMAGE_CONTEXT *Context, IN VOID *BlockIoHandle)
BOOLEAN OcAppleDiskImageInitializeFromFile(OUT OC_APPLE_DISK_IMAGE_CONTEXT *Context, IN EFI_FILE_PROTOCOL *File)
EFI_HANDLE OcAppleDiskImageInstallBlockIo(IN OC_APPLE_DISK_IMAGE_CONTEXT *Context, IN UINTN FileSize, OUT CONST EFI_DEVICE_PATH_PROTOCOL **DevicePath OPTIONAL, OUT UINTN *DevicePathSize OPTIONAL)
VOID OcAppleDiskImageFreeContext(IN OC_APPLE_DISK_IMAGE_CONTEXT *Context)
VOID OcAppleDiskImageFreeFile(IN OC_APPLE_DISK_IMAGE_CONTEXT *Context)
CONST APPLE_PK_ENTRY PkDataBase[NUM_OF_PK]
VOID OcAppleSecureBootSetDmgLoading(IN BOOLEAN LoadingDmg)
OC_DMG_LOADING_SUPPORT
@ OcDmgLoadingAppleSigned
EFI_BOOT_SERVICES * gBS
EFI_STATUS EFIAPI OcOpenFileByDevicePath(IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath, OUT EFI_FILE_PROTOCOL **File, IN UINT64 OpenMode, IN UINT64 Attributes)
Definition OpenFile.c:206
EFI_STATUS OcGetFileSize(IN EFI_FILE_PROTOCOL *File, OUT UINT32 *Size)
EFI_STATUS OcGetFileData(IN EFI_FILE_PROTOCOL *File, IN UINT32 Position, IN UINT32 Size, OUT UINT8 *Buffer)
EFI_STATUS OcSafeFileOpen(IN CONST EFI_FILE_PROTOCOL *Directory, OUT EFI_FILE_PROTOCOL **NewHandle, IN CONST CHAR16 *FileName, IN CONST UINT64 OpenMode, IN CONST UINT64 Attributes)
Definition OpenFile.c:29
#define L_STR_LEN(String)
Definition OcStringLib.h:26
INTN EFIAPI CompareMem(IN CONST VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
EFI_GUID gEfiSimpleFileSystemProtocolGuid
EFI_GUID gEfiDevicePathProtocolGuid
#define ASSERT(x)
Definition coder.h:55