diff --git a/.gitignore b/.gitignore index 6336c153..9dfad74e 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ Legacy/BinDrivers/X64 Legacy/BinDrivers/IA32 Legacy/BootLoader/bin Legacy/BootSector/bin +Utilities/ocvalidate/*.plist diff --git a/Include/Acidanthera/Library/OcBootManagementLib.h b/Include/Acidanthera/Library/OcBootManagementLib.h index 8de8fbf8..dda71af9 100755 --- a/Include/Acidanthera/Library/OcBootManagementLib.h +++ b/Include/Acidanthera/Library/OcBootManagementLib.h @@ -62,6 +62,10 @@ typedef struct OC_PICKER_CONTEXT_ OC_PICKER_CONTEXT; #define OC_ATTR_USE_GENERIC_LABEL_IMAGE BIT2 #define OC_ATTR_USE_ALTERNATE_ICONS BIT3 #define OC_ATTR_USE_POINTER_CONTROL BIT4 +#define OC_ATTR_ALL_BITS (\ + OC_ATTR_USE_VOLUME_ICON | OC_ATTR_USE_DISK_LABEL_FILE | \ + OC_ATTR_USE_GENERIC_LABEL_IMAGE | OC_ATTR_USE_ALTERNATE_ICONS | \ + OC_ATTR_USE_POINTER_CONTROL) /** Default timeout for IDLE timeout during menu picker navigation diff --git a/Include/Acidanthera/Library/OcConfigurationLib.h b/Include/Acidanthera/Library/OcConfigurationLib.h index 98c1e42b..43502c1f 100644 --- a/Include/Acidanthera/Library/OcConfigurationLib.h +++ b/Include/Acidanthera/Library/OcConfigurationLib.h @@ -349,6 +349,9 @@ #define OCS_EXPOSE_VERSION_UI 4U #define OCS_EXPOSE_OEM_INFO 8U #define OCS_EXPOSE_VERSION (OCS_EXPOSE_VERSION_VAR | OCS_EXPOSE_VERSION_UI) +#define OCS_EXPOSE_ALL_BITS (\ + OCS_EXPOSE_BOOT_PATH | OCS_EXPOSE_VERSION_VAR | \ + OCS_EXPOSE_VERSION_UI | OCS_EXPOSE_OEM_INFO) typedef enum { OcsVaultOptional = 0, diff --git a/Include/Acidanthera/Library/OcMacInfoLib.h b/Include/Acidanthera/Library/OcMacInfoLib.h index a72bcd03..c936fe5f 100644 --- a/Include/Acidanthera/Library/OcMacInfoLib.h +++ b/Include/Acidanthera/Library/OcMacInfoLib.h @@ -91,10 +91,22 @@ typedef struct MAC_INFO_DATA_ { **/ VOID GetMacInfo ( - IN CONST CHAR8 *ProductName, + IN CONST CHAR8 *ProductName, OUT MAC_INFO_DATA *MacInfo ); +/** + Determine if specified product name is a real Mac model. + + @param[in] ProductName Product to check information for. + + @retval TRUE if ProductName is a real Mac model. +**/ +BOOLEAN +HasMacInfo ( + IN CONST CHAR8 *ProductName + ); + /** Determine if specified model and kernel version can run in 64-bit kernel mode. diff --git a/Include/Acidanthera/Library/OcStringLib.h b/Include/Acidanthera/Library/OcStringLib.h index 98655d3d..cbb756a2 100755 --- a/Include/Acidanthera/Library/OcStringLib.h +++ b/Include/Acidanthera/Library/OcStringLib.h @@ -148,11 +148,55 @@ OcAsciiSafeSPrint ( ... ); +/** + Compares up to a specified length the contents of two Null-terminated ASCII + strings using case insensitive comparisons, and returns the difference + between the first mismatched ASCII characters. + + This function compares the Null-terminated ASCII string FirstString to the + Null-terminated ASCII string SecondString using case insensitive + comparisons. At most, Length ASCII characters will be compared. If Length + is 0, then 0 is returned. If FirstString is identical to SecondString, then 0 + is returned. Otherwise, the value returned is the first mismatched upper case + ASCII character in SecondString subtracted from the first mismatched upper + case ASCII character in FirstString. + + If Length > 0 and FirstString is NULL, then ASSERT(). + If Length > 0 and SecondString is NULL, then ASSERT(). + TODO + If PcdMaximumAsciiStringLength is not zero, and Length is greater than + PcdMaximumAsciiStringLength, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and FirstString contains more + than PcdMaximumAsciiStringLength ASCII characters, not including the + Null-terminator, then ASSERT(). + If PcdMaximumAsciiStringLength is not zero, and SecondString contains more + than PcdMaximumAsciiStringLength ASCII characters, not including the + Null-terminator, then ASSERT(). + + @param FirstString A pointer to a Null-terminated ASCII string. + @param SecondString A pointer to a Null-terminated ASCII string. + @param Length The maximum number of ASCII characters to compare. + + @retval ==0 FirstString is identical to SecondString using case + insensitive comparisons. + @retval others FirstString is not identical to SecondString using case + insensitive comparisons. + +**/ +INTN +EFIAPI +OcAsciiStrniCmp ( + IN CONST CHAR8 *FirstString, + IN CONST CHAR8 *SecondString, + IN UINTN Length + ); + /** Check if ASCII string ends with another ASCII string. - @param[in] String A pointer to a Null-terminated ASCII string. - @param[in] SearchString A pointer to a Null-terminated ASCII string - to compare against String. + @param[in] String A pointer to a Null-terminated ASCII string. + @param[in] SearchString A pointer to a Null-terminated ASCII string + to compare against String. + @param[in] CaseInsensitiveMatch Perform case-insensitive comparison. @retval TRUE if String ends with SearchString. **/ @@ -160,7 +204,8 @@ BOOLEAN EFIAPI OcAsciiEndsWith ( IN CONST CHAR8 *String, - IN CONST CHAR8 *SearchString + IN CONST CHAR8 *SearchString, + IN BOOLEAN CaseInsensitiveMatch ); /** @@ -396,9 +441,10 @@ OcUnicodeSafeSPrint ( /** Check if Unicode string ends with another Unicode string. - @param[in] String A pointer to a Null-terminated Unicode string. - @param[in] SearchString A pointer to a Null-terminated Unicode string - to compare against String. + @param[in] String A pointer to a Null-terminated Unicode string. + @param[in] SearchString A pointer to a Null-terminated Unicode string + to compare against String. + @param[in] CaseInsensitiveMatch Perform case-insensitive comparison. @retval TRUE if String ends with SearchString. **/ @@ -406,7 +452,8 @@ BOOLEAN EFIAPI OcUnicodeEndsWith ( IN CONST CHAR16 *String, - IN CONST CHAR16 *SearchString + IN CONST CHAR16 *SearchString, + IN BOOLEAN CaseInsensitiveMatch ); /** diff --git a/Include/Acidanthera/Protocol/OcLog.h b/Include/Acidanthera/Protocol/OcLog.h index 0a089743..260ded36 100755 --- a/Include/Acidanthera/Protocol/OcLog.h +++ b/Include/Acidanthera/Protocol/OcLog.h @@ -33,6 +33,11 @@ #define OC_LOG_VARIABLE BIT4 #define OC_LOG_NONVOLATILE BIT5 #define OC_LOG_FILE BIT6 +#define OC_LOG_ALL_BITS (\ + OC_LOG_ENABLE | OC_LOG_CONSOLE | \ + OC_LOG_DATA_HUB | OC_LOG_SERIAL | \ + OC_LOG_VARIABLE | OC_LOG_NONVOLATILE | \ + OC_LOG_FILE) typedef UINT32 OC_LOG_OPTIONS; diff --git a/Library/OcAppleKernelLib/CachelessContext.c b/Library/OcAppleKernelLib/CachelessContext.c index 8432b2c0..2941ea72 100644 --- a/Library/OcAppleKernelLib/CachelessContext.c +++ b/Library/OcAppleKernelLib/CachelessContext.c @@ -184,7 +184,7 @@ ScanExtensions ( } if (FileInfoSize > 0) { - if (OcUnicodeEndsWith (FileInfo->FileName, L".kext")) { + if (OcUnicodeEndsWith (FileInfo->FileName, L".kext", FALSE)) { Status = File->Open (File, &FileKext, FileInfo->FileName, EFI_FILE_MODE_READ, EFI_FILE_DIRECTORY); if (EFI_ERROR (Status)) { continue; @@ -1391,7 +1391,7 @@ CachelessContextHookBuiltin ( // // Try to get Info.plist. // - if (OcUnicodeEndsWith (FileName, L"Info.plist")) { + if (OcUnicodeEndsWith (FileName, L"Info.plist", FALSE)) { BuiltinKext = LookupBuiltinKextForPlistPath (Context, FileName); if (BuiltinKext != NULL && BuiltinKext->PatchValidOSBundleRequired) { DEBUG ((DEBUG_INFO, "OCAK: Processing plist patches for %s\n", FileName)); diff --git a/Library/OcMacInfoLib/OcMacInfoLib.c b/Library/OcMacInfoLib/OcMacInfoLib.c index 31bd61f2..5d6a0533 100755 --- a/Library/OcMacInfoLib/OcMacInfoLib.c +++ b/Library/OcMacInfoLib/OcMacInfoLib.c @@ -84,11 +84,11 @@ LookupInternalEntry ( // // Even the first element does not match, required due to unsigned End. // - return &gMacInfoModels[gMacInfoDefaultModel]; + return NULL; } } - return &gMacInfoModels[gMacInfoDefaultModel]; + return NULL; } VOID @@ -102,6 +102,12 @@ GetMacInfo ( ZeroMem (MacInfo, sizeof (*MacInfo)); InternalEntry = LookupInternalEntry (ProductName); + if (InternalEntry == NULL) { + // + // Fallback to default model if given ProductName is not found. + // + InternalEntry = &gMacInfoModels[gMacInfoDefaultModel]; + } // // Fill in DataHub values. @@ -145,6 +151,18 @@ GetMacInfo ( } } +BOOLEAN +HasMacInfo ( + IN CONST CHAR8 *ProductName + ) +{ + CONST MAC_INFO_INTERNAL_ENTRY *InternalEntry; + + InternalEntry = LookupInternalEntry (ProductName); + + return InternalEntry != NULL; +} + BOOLEAN IsMacModel64BitCompatible ( IN CONST CHAR8 *ProductName, diff --git a/Library/OcStringLib/OcAsciiLib.c b/Library/OcStringLib/OcAsciiLib.c index 1443a690..0f344f3a 100755 --- a/Library/OcStringLib/OcAsciiLib.c +++ b/Library/OcStringLib/OcAsciiLib.c @@ -202,11 +202,50 @@ OcAsciiSafeSPrint ( return Status; } +INTN +EFIAPI +OcAsciiStrniCmp ( + IN CONST CHAR8 *FirstString, + IN CONST CHAR8 *SecondString, + IN UINTN Length + ) +{ + CHAR8 UpperFirstString; + CHAR8 UpperSecondString; + + if (Length == 0) { + return 0; + } + + // + // ASSERT both strings are less long than PcdMaximumAsciiStringLength. + // Length tests are performed inside AsciiStrLen(). + // + ASSERT (AsciiStrSize (FirstString) != 0); + ASSERT (AsciiStrSize (SecondString) != 0); + + UpperFirstString = AsciiCharToUpper (*FirstString); + UpperSecondString = AsciiCharToUpper (*SecondString); + while ((*FirstString != '\0') && + (*SecondString != '\0') && + (UpperFirstString == UpperSecondString) && + (Length > 1)) { + FirstString++; + SecondString++; + UpperFirstString = AsciiCharToUpper (*FirstString); + UpperSecondString = AsciiCharToUpper (*SecondString); + Length--; + } + + return UpperFirstString - UpperSecondString; +} + BOOLEAN EFIAPI OcAsciiEndsWith ( IN CONST CHAR8 *String, - IN CONST CHAR8 *SearchString + IN CONST CHAR8 *SearchString, + IN BOOLEAN CaseInsensitiveMatch ) { UINTN StringLength; @@ -218,6 +257,10 @@ OcAsciiEndsWith ( StringLength = AsciiStrLen (String); SearchStringLength = AsciiStrLen (SearchString); + if (CaseInsensitiveMatch) { + return StringLength >= SearchStringLength + && OcAsciiStrniCmp (&String[StringLength - SearchStringLength], SearchString, SearchStringLength) == 0; + } return StringLength >= SearchStringLength && AsciiStrnCmp (&String[StringLength - SearchStringLength], SearchString, SearchStringLength) == 0; } diff --git a/Library/OcStringLib/OcStringLib.inf b/Library/OcStringLib/OcStringLib.inf index 7c855c0c..97974d2e 100755 --- a/Library/OcStringLib/OcStringLib.inf +++ b/Library/OcStringLib/OcStringLib.inf @@ -38,9 +38,6 @@ OpenCorePkg/OpenCorePkg.dec MdePkg/MdePkg.dec -[Pcd] - gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength ## SOMETIMES_CONSUMES - [LibraryClasses] BaseLib BaseMemoryLib diff --git a/Library/OcStringLib/OcUnicodeLib.c b/Library/OcStringLib/OcUnicodeLib.c index 2af62574..1a612152 100755 --- a/Library/OcStringLib/OcUnicodeLib.c +++ b/Library/OcStringLib/OcUnicodeLib.c @@ -71,11 +71,7 @@ OcStrniCmp ( // ASSERT (StrSize (FirstString) != 0); ASSERT (StrSize (SecondString) != 0); - - if (PcdGet32 (PcdMaximumUnicodeStringLength) != 0) { - ASSERT (Length <= PcdGet32 (PcdMaximumUnicodeStringLength)); - } - + UpperFirstString = CharToUpper (*FirstString); UpperSecondString = CharToUpper (*SecondString); while ((*FirstString != L'\0') && @@ -360,7 +356,8 @@ BOOLEAN EFIAPI OcUnicodeEndsWith ( IN CONST CHAR16 *String, - IN CONST CHAR16 *SearchString + IN CONST CHAR16 *SearchString, + IN BOOLEAN CaseInsensitiveMatch ) { UINTN StringLength; @@ -372,6 +369,10 @@ OcUnicodeEndsWith ( StringLength = StrLen (String); SearchStringLength = StrLen (SearchString); + if (CaseInsensitiveMatch) { + return StringLength >= SearchStringLength + && OcStrniCmp (&String[StringLength - SearchStringLength], SearchString, SearchStringLength) == 0; + } return StringLength >= SearchStringLength && StrnCmp (&String[StringLength - SearchStringLength], SearchString, SearchStringLength) == 0; } diff --git a/User/Makefile b/User/Makefile index bbe3d67a..8c79cbd4 100644 --- a/User/Makefile +++ b/User/Makefile @@ -155,7 +155,11 @@ ifneq ($(STANDALONE),1) # # OcMiscLib targets. # - OBJS += Math.o ProtocolSupport.o + OBJS += Math.o ProtocolSupport.o DataPatcher.o + # + # OcAppleKernelLib targets. + # + OBJS += KernelVersion.o # # Add source searchpath for transparent compilation. @@ -183,7 +187,8 @@ ifneq ($(STANDALONE),1) ../../Library/OcMachoLib:$\ ../../Library/OcAppleKeysLib:$\ ../../Library/OcCpuLib:$\ - ../../Library/OcMiscLib + ../../Library/OcMiscLib:$\ + ../../Library/OcAppleKernelLib endif # diff --git a/Utilities/TestKextInject/Makefile b/Utilities/TestKextInject/Makefile index 579719e6..68ec5eaf 100644 --- a/Utilities/TestKextInject/Makefile +++ b/Utilities/TestKextInject/Makefile @@ -8,7 +8,6 @@ PRODUCT = $(PROJECT)$(SUFFIX) OBJS = $(PROJECT).o \ CommonPatches.o \ CpuidPatches.o \ - KernelVersion.o \ KextPatcher.o \ KxldState.o \ PrelinkedKext.o \ @@ -18,7 +17,6 @@ OBJS = $(PROJECT).o \ Link.o \ KernelReader.o \ KernelCollection.o \ - DataPatcher.o \ lzss.o \ lzvn.o \ adler32.o \ @@ -33,7 +31,6 @@ OBJS = $(PROJECT).o \ uncompr.o \ zlib_uefi.o VPATH = ../../Library/OcAppleKernelLib:$\ - ../../Library/OcMiscLib:$\ ../../Library/OcCompressionLib/lzss:$\ ../../Library/OcCompressionLib/lzvn:$\ ../../Library/OcCompressionLib/zlib diff --git a/Utilities/ocvalidate/Checklist.md b/Utilities/ocvalidate/Checklist.md new file mode 100644 index 00000000..fc21737a --- /dev/null +++ b/Utilities/ocvalidate/Checklist.md @@ -0,0 +1,87 @@ +ocvalidate Checklist +===================== + +As of commit [TODO_DONT_CLICK](TODO), ocvalidate performs the following checks: + +## Global Rules +- For all strings (fields with plist `String` format) throughout the whole config, only ASCII printable characters are accepted at most. Stricter rules may apply. For instance, some fields only accept specified values, as indicated in [Configuration.pdf](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf). +- For all patches, excluding section `Kernel->Patch` (where `Base` is not empty), their `Find`, `Replace`, `Mask`, and `ReplaceMask` must have identical size in most cases. Also, `Find` requires `Mask` (or `Replace` requires `ReplaceMask`) to be active (set to non-zero) for corresponding bits. +- For all `MinKernel` and `MaxKernel` settings, they should follow the conventions indicated in [Configuration.pdf](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf). (TODO: Bring decent checks for this) +- For all entries taking file system path only `0-9, A-Z, a-z, '_', '-', '.', '/', and '\'` are accepted. +- For all Device Paths (e.g. `PciRoot(0x0)/Pci(0x1b,0x0)`) only strings in canonic string format are accepted. +- For all paths of UEFI Drivers, only `0-9, A-Z, a-z, '_', '-', '.', and '/'` are accepted. +- For all entries requiring bitwise operations (e.g. `ConsoleAttributes`, `PickerAttributes`, or `ScanPolicy`), only known bits can be set. +- For all entries involving GUID (mainly at Section `NVRAM`), correct format must be ensured. + +## ACPI +#### Add +- Entry[N]->Path: Only `.aml` and `.bin` filename suffix are accepted. +- Entry[N]->Path: If a customised DSDT is added and enabled, `RebaseRegions` in `Quirks` should be enabled. + +## Booter +#### MmioWhitelist +- Entry[N]->Enabled: When at least one entry is enabled, `DevirtualiseMmio` in `Quirks` should be enabled. +#### Patch +- Entry[N]->Arch: Only `Any`, `i386`, or `x86_64` are accepted. +- Entry[N]->Identifier: Only `Any`, `Apple`, or a specified bootloader with `.efi` sufffix, are accepted. +#### Quirks +- When `AllowRelocationBlock` is enabled, `ProvideCustomSlide` should be enabled altogether. +- When `EnableSafeModeSlide` is enabled, `ProvideCustomSlide` should be enabled altogether. +- If `ProvideMaxSlide` is set to a number greater than zero (i.e. is enabled), `ProvideCustomSlide` should be enabled altogether. +- When `DisableVariableWrite`, `EnableWriteUnprotector`, or `ProvideCustomSlide` is enabled, `OpenRuntime.efi` should be loaded under `UEFI->Drivers`. + +## DeviceProperties +- Check requirements for Device Paths in Section Global Rules. + +## Kernel +#### Add +- Entry[N]->Arch: Only `Any`, `i386`, or `x86_64` are accepted. +- Entry[N]->BundlePath: Filename should have `.kext` suffix. +- Entry[N]->PlistPath: Filename should have `.plist` suffix. +- Entry[N]: If `Lilu.kext` is used, `DisableLinkeditJettison` should be enabled at `Kernel->Quirks`. +- For some known kexts, their `BundlePath`, `ExecutablePath`, and `PlistPath` must match against each other. Current list of rules can be found [here](https://github.com/PMheart/OpenCorePkg/blob/master/Utilities/ocvalidate/ValidateKernel.c). (TODO: Change to Acidanthera link once merged) +- Known [Lilu plugins](https://github.com/acidanthera/Lilu/blob/master/KnownPlugins.md) must have proper `Add` precedence. That is to say, plugins must be placed after `Lilu.kext`. +#### Delete +- Entry[N]->Arch: Only `Any`, `i386`, or `x86_64` are accepted. +- Entry[N]->Identifier: At least one dot (`.`) should exist, because any identifier looks like a domain sequence (`vendor.product`). +#### Quirks +- `CustomSMBIOSGuid` requires `UpdateSMBIOSMode` at `PlatformInfo` set to `Custom`. +#### Scheme +- KernelArch: Only `Auto`, `i386`, `i386-user32`, or `x86_64` are accepted. +- KernelCache: Only `Auto`, `Cacheless`, `Mkext`, or `Prelinked` are accepted. + +## Misc +#### Boot +- HibernateMode: Only `None`, `Auto`, `RTC`, or `NVRAM` are accepted. +- PickerMode: Only `Builtin`, `External`, or `Apple` are accepted. +#### Security +- BootProtect: Only `None`, `Bootstrap`, or `BootstrapShort` are accepted. When set to `Bootstrap` or `BootstrapShort`, `RequestBootVarRouting` should be enabled at `UEFI->Quirks`. +- DmgLoading: Only `Disabled`, `Signed`, or `Any` are accepted. +- Vault: Only `Optional`, `Basic`, or `Secure` are accepted. +- SecureBootModel: Only `Default`, `Disabled`, `j137`, `j680`, `j132`, `j174`, `j140k`, `j780`, `j213`, `j140a`, `j152f`, `j160`, `j230k`, `j214k`, `j223`, `j215`, `j185`, `j185f`, or `x86legacy` are accepted. + +## NVRAM +- Check requirements for GUID in Section Global Rules. + +## PlatformInfo +- UpdateSMBIOSMode: Only `TryOverwrite`, `Create`, `Overwrite`, or `Custom` are accepted. +#### Generic +- SystemProductName: Only real Mac models are accepted. +- SystemMemoryStatus: Only `Auto`, `Upgradable`, or `Soldered` are accepted. + +## UEFI +#### APFS +- When `EnableJumpstart` is enabled, `ScanPolicy` at `Misc->Security` should have `OC_SCAN_ALLOW_FS_APFS` (bit 8) set, or `ScanPolicy` should be `0` (failsafe value). +#### Quirks +- When `RequestBootVarRouting` is enabled, `OpenRuntime.efi` should be loaded under `UEFI->Drivers`. +#### Drivers +- No drivers should be loaded more than once (i.e. there should NOT be any duplicated entries in this section). +- When `OpenUsbKbDxe.efi` is in use, `KeySupport` at `UEFI->Input` should NEVER be enabled altogether. +- When `Ps2KeyboardDxe.efi` is in use, `KeySupport` at `UEFI->Input` should be enabled altogether. +- `OpenUsbKbDxe.efi` and `Ps2KeyboardDxe.efi` should never co-exist. +#### Input +- The value of `KeySupportMode` can only be `Auto`, `V1`, `V2`, or `AMI`. +- When `PointerSupport` is enabled, the value of `PointerSupportMode` should only be `ASUS`. +#### Output +- `ClearScreenOnModeSwitch`, `IgnoreTextInGraphics`, `ReplaceTabWithSpace`, and `SanitiseClearScreen` only apply to `System` TextRenderer +- `Resolution` should match `NUMBERxNUMBER` or `NUMBERxNUMBER@NUMBER` sequences (unless it is an `Empty string` or is set to `Max`). diff --git a/Utilities/ocvalidate/Makefile b/Utilities/ocvalidate/Makefile index 2c4bec60..fad2f083 100644 --- a/Utilities/ocvalidate/Makefile +++ b/Utilities/ocvalidate/Makefile @@ -5,7 +5,21 @@ PROJECT = ocvalidate PRODUCT = $(PROJECT)$(SUFFIX) -OBJS = $(PROJECT).o \ - OcConfigurationLib.o -VPATH = ../../Library/OcConfigurationLib +OBJS = $(PROJECT).o OcValidateLib.o ValidateAcpi.o ValidateBooter.o ValidateDeviceProperties.o ValidateKernel.o ValidateMisc.o ValidateNVRAM.o ValidatePlatformInfo.o ValidateUEFI.o +# +# OcConfigurationLib targets. +# +OBJS += OcConfigurationLib.o +# +# OcConsoleLib targets. +# +OBJS += ResolutionParsing.o +# +# OcMacInfoLib targets. +# +OBJS += OcMacInfoLib.o AutoGenerated.o + +VPATH = ../../Library/OcConfigurationLib \ + ../../Library/OcConsoleLib \ + ../../Library/OcMacInfoLib include ../../User/Makefile diff --git a/Utilities/ocvalidate/OcValidateLib.c b/Utilities/ocvalidate/OcValidateLib.c new file mode 100644 index 00000000..4e8659e4 --- /dev/null +++ b/Utilities/ocvalidate/OcValidateLib.c @@ -0,0 +1,418 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +INT64 +GetCurrentTimestamp ( + VOID + ) +{ + struct timeval Time; + // + // Get current time. + // + gettimeofday (&Time, NULL); + // + // Return milliseconds. + // + return Time.tv_sec * 1000LL + Time.tv_usec / 1000LL; +} + +BOOLEAN +AsciiFileSystemPathIsLegal ( + IN CONST CHAR8 *Path + ) +{ + UINTN Index; + UINTN PathLength; + + PathLength = AsciiStrLen (Path); + + for (Index = 0; Index < PathLength; ++Index) { + // + // Skip allowed characters (0-9, A-Z, a-z, '_', '-', '.', '/', and '\'). + // + if (IsAsciiNumber (Path[Index]) + || IsAsciiAlpha (Path[Index]) + || Path[Index] == '_' + || Path[Index] == '-' + || Path[Index] == '.' + || Path[Index] == '/' + || Path[Index] == '\\') { + continue; + } + + // + // Disallowed characters matched. + // + return FALSE; + } + + return TRUE; +} + +BOOLEAN +AsciiCommentIsLegal ( + IN CONST CHAR8 *Comment + ) +{ + UINTN Index; + UINTN CommentLength; + + CommentLength = AsciiStrLen (Comment); + + for (Index = 0; Index < CommentLength; ++Index) { + // + // Unprintable characters matched. + // + if (IsAsciiPrint (Comment[Index]) == 0) { + return FALSE; + } + } + + return TRUE; +} + +BOOLEAN +AsciiIdentifierIsLegal ( + IN CONST CHAR8 *Identifier, + IN BOOLEAN IsKernelIdentifier + ) +{ + UINTN Index; + UINTN IdentifierLength; + + if (IsKernelIdentifier) { + // + // Kernel patch only requires Kernel->Patch->Identifier set to kernel. + // + if (AsciiStrCmp (Identifier, "kernel") == 0) { + return TRUE; + } + } else { + // + // Any and Apple are two fixed values accepted by Booter->Patch. + // TODO: Drop empty string support in OC. + // + if (AsciiStrCmp (Identifier, "Any") == 0 + || AsciiStrCmp (Identifier, "Apple") == 0) { + return TRUE; + } + // + // For customised bootloader, it must have .efi suffix. + // + if (!OcAsciiEndsWith (Identifier, ".efi", TRUE)) { + return FALSE; + } + } + + // + // There must be a dot for a sane Identifier. + // + if (OcAsciiStrChr (Identifier, '.') == NULL) { + return FALSE; + } + + IdentifierLength = AsciiStrLen (Identifier); + + for (Index = 0; Index < IdentifierLength; ++Index) { + // + // Skip allowed characters (0-9, A-Z, a-z, '_', '-', and '.'). + // FIXME: Discuss what exactly is legal for identifiers, or update the allowed list on request. + // + if (IsAsciiNumber (Identifier[Index]) + || IsAsciiAlpha (Identifier[Index]) + || Identifier[Index] == '_' + || Identifier[Index] == '-' + || Identifier[Index] == '.') { + continue; + } + + // + // Disallowed characters matched. + // + return FALSE; + } + + return TRUE; +} + +BOOLEAN +AsciiArchIsLegal ( + IN CONST CHAR8 *Arch, + IN BOOLEAN IsKernelArch + ) +{ + // + // Special mode for Kernel->Scheme->KernelArch. + // + if (IsKernelArch) { + // + // Auto and i386-user32 are two special values allowed in KernelArch. + // + if (AsciiStrCmp (Arch, "Auto") == 0 + || AsciiStrCmp (Arch, "i386-user32") == 0) { + return TRUE; + } + } else { + // + // Any is only allowed in non-KernelArch mode. + // + if (AsciiStrCmp (Arch, "Any") == 0) { + return TRUE; + } + } + + // + // i386 and x86_64 are allowed in both modes. + // TODO: Do not allow empty string in OC. + // + if (AsciiStrCmp (Arch, "i386") != 0 + && AsciiStrCmp (Arch, "x86_64") != 0) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +AsciiPropertyIsLegal ( + IN CONST CHAR8 *Property + ) +{ + // + // Like comments, properties can be anything printable. + // Calling sanitiser for comments to reduce code duplication. + // + return AsciiCommentIsLegal (Property); +} + +BOOLEAN +AsciiUefiDriverIsLegal ( + IN CONST CHAR8 *Driver + ) +{ + UINTN Index; + UINTN DriverLength; + + // + // If an EFI driver does not contain .efi suffix, + // then it must be illegal. + // + if (!OcAsciiEndsWith (Driver, ".efi", TRUE)) { + return FALSE; + } + + DriverLength = AsciiStrLen (Driver); + + for (Index = 0; Index < DriverLength; ++Index) { + // + // NOTE: Skip '#' as it is treated as comments and thus is legal. + // + if (Driver[0] == '#') { + continue; + } + + // + // Skip allowed characters (0-9, A-Z, a-z, '_', '-', '.', '/'). + // + if (IsAsciiNumber (Driver[Index]) + || IsAsciiAlpha (Driver[Index]) + || Driver[Index] == '_' + || Driver[Index] == '-' + || Driver[Index] == '.' + || Driver[Index] == '/') { + continue; + } + + // + // Disallowed characters matched. + // + return FALSE; + } + + return TRUE; +} + +BOOLEAN +AsciiDevicePathIsLegal ( + IN CONST CHAR8 *AsciiDevicePath + ) +{ + BOOLEAN RetVal; + CHAR16 *UnicodeDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + CHAR16 *TextualDevicePath; + + RetVal = TRUE; + + // + // Convert ASCII device path to Unicode format. + // + UnicodeDevicePath = AsciiStrCopyToUnicode (AsciiDevicePath, 0); + if (UnicodeDevicePath != NULL) { + // + // Firstly, convert Unicode device path to binary. + // + DevicePath = ConvertTextToDevicePath (UnicodeDevicePath); + if (DevicePath != NULL) { + // + // Secondly, convert binary back to Unicode device path. + // + TextualDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE); + if (TextualDevicePath != NULL) { + // + // If the results before and after conversion do not match, + // then the original device path is borked. + // + if (OcStriCmp (UnicodeDevicePath, TextualDevicePath) != 0) { + DEBUG (( + DEBUG_WARN, + "Original path: %s\nPath after internal conversion: %s\n\n", + UnicodeDevicePath, + TextualDevicePath + )); + // + // Do not return immediately in order to free properly. + // + RetVal = FALSE; + } + FreePool (TextualDevicePath); + } + FreePool (DevicePath); + } + FreePool (UnicodeDevicePath); + } + + return RetVal; +} + +BOOLEAN +DataHasProperMasking ( + IN CONST VOID *Data, + IN CONST VOID *Mask, + IN UINTN Size + ) +{ + CONST UINT8 *ByteData; + CONST UINT8 *ByteMask; + UINTN Index; + + ByteData = Data; + ByteMask = Mask; + + for (Index = 0; Index < Size; ++Index) { + if ((ByteData[Index] & ~ByteMask[Index]) != 0) { + return FALSE; + } + } + + return TRUE; +} + +UINT32 +ValidatePatch ( + IN CONST CHAR8 *PatchSection, + IN UINT32 PatchIndex, + IN BOOLEAN FindSizeCanBeZero, + IN CONST UINT8 *Find, + IN UINT32 FindSize, + IN CONST UINT8 *Replace, + IN UINT32 ReplaceSize, + IN CONST UINT8 *Mask, + IN UINT32 MaskSize, + IN CONST UINT8 *ReplaceMask, + IN UINT32 ReplaceMaskSize + ) +{ + UINT32 ErrorCount; + + ErrorCount = 0; + + if (!FindSizeCanBeZero && FindSize != ReplaceSize) { + DEBUG (( + DEBUG_WARN, + "%a[%u] has different Find and Replace size (%u vs %u)!\n", + PatchSection, + PatchIndex, + FindSize, + ReplaceSize + )); + ++ErrorCount; + } + + if (MaskSize > 0) { + if (MaskSize != FindSize) { + DEBUG (( + DEBUG_WARN, + "%a[%u] has Mask set but its size is different from Find (%u vs %u)!\n", + PatchSection, + PatchIndex, + MaskSize, + FindSize + )); + ++ErrorCount; + } else if (!DataHasProperMasking (Find, Mask, FindSize)) { + DEBUG (( + DEBUG_WARN, + "%a[%u]->Find requires Mask to be active for corresponding bits!\n", + PatchSection, + PatchIndex + )); + ++ErrorCount; + } + } + + if (ReplaceMaskSize > 0) { + if (ReplaceMaskSize != ReplaceSize) { + DEBUG (( + DEBUG_WARN, + "%a[%u] has ReplaceMask set but its size is different from Replace (%u vs %u)!\n", + PatchSection, + PatchIndex, + ReplaceMaskSize, + ReplaceSize + )); + ++ErrorCount; + } else if (!DataHasProperMasking (Replace, ReplaceMask, ReplaceSize)) { + DEBUG (( + DEBUG_WARN, + "%a[%u]->Replace requires ReplaceMask to be active for corresponding bits!\n", + PatchSection, + PatchIndex + )); + ++ErrorCount; + } + } + + return ErrorCount; +} + +UINT32 +ReportError ( + IN CONST CHAR8 *FuncName, + IN UINT32 ErrorCount + ) +{ + if (ErrorCount != 0) { + DEBUG ((DEBUG_WARN, "%a returns %u %a!\n", FuncName, ErrorCount, ErrorCount > 1 ? "errors" : "error")); + } else { + DEBUG ((DEBUG_VERBOSE, "%a returns no errors!\n", FuncName)); + } + + return ErrorCount; +} diff --git a/Utilities/ocvalidate/OcValidateLib.h b/Utilities/ocvalidate/OcValidateLib.h new file mode 100644 index 00000000..960bdd58 --- /dev/null +++ b/Utilities/ocvalidate/OcValidateLib.h @@ -0,0 +1,190 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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. +**/ + +#ifndef OC_USER_UTILITIES_OCVALIDATELIB_H +#define OC_USER_UTILITIES_OCVALIDATELIB_H + +#include + +#include +#include + +/** + Get current timestamp in milliseconds. + + @return Current timestamp in milliseconds. +**/ +INT64 +GetCurrentTimestamp ( + VOID + ); + +/** + Check if a filesystem path contains only legal characters. + + @param[in] Path Filesystem path to be checked. + + @retval TRUE If Path only contains 0-9, A-Z, a-z, '_', '-', '.', '/', and '\'. +**/ +BOOLEAN +AsciiFileSystemPathIsLegal ( + IN CONST CHAR8 *Path + ); + +/** + Check if an OpenCore Configuration Comment contains only ASCII printable characters. + + @param[in] Comment Comment to be checked. + + @retval TRUE If Comment only contains ASCII printable characters. +**/ +BOOLEAN +AsciiCommentIsLegal ( + IN CONST CHAR8 *Comment + ); + +/** + Check if an OpenCore Configuration Identifier matches specific conventions. + + @param[in] Identifier Identifier to be checked. + @param[in] IsKernelPatchIdentifier TRUE to perform special checks for Kernel->Patch->Identifier. + + @retval TRUE If Identifier matches conventions. +**/ +BOOLEAN +AsciiIdentifierIsLegal ( + IN CONST CHAR8 *Identifier, + IN BOOLEAN IsKernelIdentifier + ); + +/** + Check if an OpenCore Configuration Arch matches specific conventions. + + @param[in] Arch Arch to be checked. + @param[in] IsKernelArch Whether to perform special checks for Kernel->Scheme->KernelArch. + + @retval TRUE If Arch matches conventions. +**/ +BOOLEAN +AsciiArchIsLegal ( + IN CONST CHAR8 *Arch, + IN BOOLEAN IsKernelArch + ); + +/** + Check if an OpenCore Configuration Property contains only ASCII printable characters. Mainly used in device properties and NVRAM properties. + + @param[in] Property Property to be checked. + + @retval TRUE If Property only contains ASCII printable characters. +**/ +BOOLEAN +AsciiPropertyIsLegal ( + IN CONST CHAR8 *Property + ); + +/** + Check if a UEFI Driver matches specific conventions. + + @param[in] Driver Driver to be checked. + + @retval TRUE If path of Driver contains .efi suffix, and only contains 0-9, A-Z, a-z, '_', '-', '.', and '/'. +**/ +BOOLEAN +AsciiUefiDriverIsLegal ( + IN CONST CHAR8 *Driver + ); + +/** + Check if a device path in ASCII is valid. + + @param[in] AsciiDevicePath Device path to be checked. + + @retval TRUE If AsciiDevicePath is valid. +**/ +BOOLEAN +AsciiDevicePathIsLegal ( + IN CONST CHAR8 *AsciiDevicePath + ); + +/** + Check if a set of data has proper masking set. + + This function assumes identical sizes of Data and Mask, which must be ensured before calling. + + @param[in] Data Data to be checked. + @param[in] Mask Mask to be paired with Data. + @param[in] Size Size of Data and Mask. + + @retval TRUE If corresponding bits of Mask to Data are active (set to non-zero). +**/ +BOOLEAN +DataHasProperMasking ( + IN CONST VOID *Data, + IN CONST VOID *Mask, + IN UINTN Size + ); + +/** + Check if an OpenCore binary patch is valid. + + If size of Find pattern cannot be zero, and size of Find pattern is different from that of Replace pattern, it is an error. + If Mask/ReplaceMask is used, but its size is different from that of Find/Replace, it is an error. + If Mask/ReplaceMask is used without corresponding bits being active for Find/Replace pattern, it is an error. + + @param[in] PatchSection Patch section to which the patch to be checked belongs. + @param[in] PatchIndex Index of the patch to be checked. + @param[in] FindSizeCanBeZero Whether size of Find pattern can be zero. Set to TRUE only when Kernel->Patch->Base is used and Find is empty. + @param[in] Find Find pattern to be checked. + @param[in] FindSize Size of Find pattern to be checked. + @param[in] Replace Replace pattern to be checked. + @param[in] ReplaceSize Size of Replace pattern to be checked. + @param[in] Mask Mask pattern to be checked. + @param[in] MaskSize Size of Mask pattern to be checked. + @param[in] ReplaceMask ReplaceMask pattern to be checked. + @param[in] ReplaceMaskSize Size of ReplaceMask pattern to be checked. + + @return Number of errors detected. +**/ +UINT32 +ValidatePatch ( + IN CONST CHAR8 *PatchSection, + IN UINT32 PatchIndex, + IN BOOLEAN FindSizeCanBeZero, + IN CONST UINT8 *Find, + IN UINT32 FindSize, + IN CONST UINT8 *Replace, + IN UINT32 ReplaceSize, + IN CONST UINT8 *Mask, + IN UINT32 MaskSize, + IN CONST UINT8 *ReplaceMask, + IN UINT32 ReplaceMaskSize + ); + +/** + Report status of errors in the end of each checker functions. + + @param[in] FuncName Checker function name. (__func__) + @param[in] ErrorCount Number of errors to be returned. + + @return Number of errors detected in one checker. +**/ +UINT32 +ReportError ( + IN CONST CHAR8 *FuncName, + IN UINT32 ErrorCount + ); + +#endif // OC_USER_UTILITIES_OCVALIDATELIB_H diff --git a/Utilities/ocvalidate/ValidateAcpi.c b/Utilities/ocvalidate/ValidateAcpi.c new file mode 100644 index 00000000..be4ec961 --- /dev/null +++ b/Utilities/ocvalidate/ValidateAcpi.c @@ -0,0 +1,140 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +UINT32 +CheckACPI ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + UINT32 Index; + OC_ACPI_CONFIG *UserAcpi; + CONST CHAR8 *Path; + CONST CHAR8 *Comment; + CONST UINT8 *Find; + UINT32 FindSize; + CONST UINT8 *Replace; + UINT32 ReplaceSize; + CONST UINT8 *Mask; + UINT32 MaskSize; + CONST UINT8 *ReplaceMask; + UINT32 ReplaceMaskSize; + BOOLEAN HasCustomDSDT; + + DEBUG ((DEBUG_VERBOSE, "config loaded into ACPI checker!\n")); + + ErrorCount = 0; + UserAcpi = &Config->Acpi; + HasCustomDSDT = FALSE; + + for (Index = 0; Index < UserAcpi->Add.Count; ++Index) { + Path = OC_BLOB_GET (&UserAcpi->Add.Values[Index]->Path); + Comment = OC_BLOB_GET (&UserAcpi->Add.Values[Index]->Comment); + + // + // Sanitise strings. + // + if (!AsciiFileSystemPathIsLegal (Path)) { + DEBUG ((DEBUG_WARN, "ACPI->Add[%u]->Path contains illegal character!\n", Index)); + ++ErrorCount; + continue; + } + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "ACPI->Add[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + + if (!OcAsciiEndsWith (Path, ".aml", TRUE) && !OcAsciiEndsWith (Path, ".bin", TRUE)) { + DEBUG ((DEBUG_WARN, "ACPI->Add[%u]->Path has filename suffix other than .aml and .bin!\n", Index)); + ++ErrorCount; + } + + if (OcAsciiStriStr (Path, "DSDT") != NULL && UserAcpi->Add.Values[Index]->Enabled) { + HasCustomDSDT = TRUE; + } + } + + for (Index = 0; Index < UserAcpi->Delete.Count; ++Index) { + Comment = OC_BLOB_GET (&UserAcpi->Delete.Values[Index]->Comment); + + // + // Sanitise strings. + // + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "ACPI->Delete[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + + // + // Size of OemTableId and TableSignature cannot be checked, + // as serialisation kills it. + // + } + + for (Index = 0; Index < UserAcpi->Patch.Count; ++Index) { + Comment = OC_BLOB_GET (&UserAcpi->Patch.Values[Index]->Comment); + Find = OC_BLOB_GET (&UserAcpi->Patch.Values[Index]->Find); + FindSize = UserAcpi->Patch.Values[Index]->Find.Size; + Replace = OC_BLOB_GET (&UserAcpi->Patch.Values[Index]->Replace); + ReplaceSize = UserAcpi->Patch.Values[Index]->Replace.Size; + Mask = OC_BLOB_GET (&UserAcpi->Patch.Values[Index]->Mask); + MaskSize = UserAcpi->Patch.Values[Index]->Mask.Size; + ReplaceMask = OC_BLOB_GET (&UserAcpi->Patch.Values[Index]->ReplaceMask); + ReplaceMaskSize = UserAcpi->Patch.Values[Index]->ReplaceMask.Size; + + // + // Sanitise strings. + // + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "ACPI->Patch[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + + // + // Size of OemTableId and TableSignature cannot be checked, + // as serialisation kills it. + // + + // + // Checks for size. + // + ErrorCount += ValidatePatch ( + "ACPI->Patch", + Index, + FALSE, + Find, + FindSize, + Replace, + ReplaceSize, + Mask, + MaskSize, + ReplaceMask, + ReplaceMaskSize + ); + } + + // + // Check for RebaseRegions when using customised DSDT. + // + if (HasCustomDSDT && !UserAcpi->Quirks.RebaseRegions) { + DEBUG ((DEBUG_WARN, "ACPI->Quirks->RebaseRegions is not enabled when customised DSDT table is in use!\n")); + ++ErrorCount; + } + + return ReportError (__func__, ErrorCount); +} diff --git a/Utilities/ocvalidate/ValidateBooter.c b/Utilities/ocvalidate/ValidateBooter.c new file mode 100644 index 00000000..249585d6 --- /dev/null +++ b/Utilities/ocvalidate/ValidateBooter.c @@ -0,0 +1,177 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +UINT32 +CheckBooter ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + UINT32 Index; + OC_BOOTER_CONFIG *UserBooter; + OC_UEFI_CONFIG *UserUefi; + CONST CHAR8 *Comment; + CONST CHAR8 *Arch; + CONST CHAR8 *Identifier; + CONST CHAR8 *Driver; + CONST UINT8 *Find; + UINT32 FindSize; + CONST UINT8 *Replace; + UINT32 ReplaceSize; + CONST UINT8 *Mask; + UINT32 MaskSize; + CONST UINT8 *ReplaceMask; + UINT32 ReplaceMaskSize; + UINT8 MaxSlide; + BOOLEAN IsMmioWhitelistEnabled; + BOOLEAN ShouldEnableDevirtualiseMmio; + BOOLEAN IsDevirtualiseMmioEnabled; + BOOLEAN IsAllowRelocationBlockEnabled; + BOOLEAN IsProvideCustomSlideEnabled; + BOOLEAN IsEnableSafeModeSlideEnabled; + BOOLEAN IsDisableVariableWriteEnabled; + BOOLEAN IsEnableWriteUnprotectorEnabled; + BOOLEAN HasOpenRuntimeEfiDriver; + + DEBUG ((DEBUG_VERBOSE, "config loaded into Booter checker!\n")); + + ErrorCount = 0; + UserBooter = &Config->Booter; + UserUefi = &Config->Uefi; + IsMmioWhitelistEnabled = FALSE; + ShouldEnableDevirtualiseMmio = FALSE; + IsDevirtualiseMmioEnabled = UserBooter->Quirks.DevirtualiseMmio; + IsAllowRelocationBlockEnabled = UserBooter->Quirks.AllowRelocationBlock; + IsProvideCustomSlideEnabled = UserBooter->Quirks.ProvideCustomSlide; + IsEnableSafeModeSlideEnabled = UserBooter->Quirks.EnableSafeModeSlide; + IsDisableVariableWriteEnabled = UserBooter->Quirks.DisableVariableWrite; + IsEnableWriteUnprotectorEnabled = UserBooter->Quirks.EnableWriteUnprotector; + HasOpenRuntimeEfiDriver = FALSE; + MaxSlide = UserBooter->Quirks.ProvideMaxSlide; + + for (Index = 0; Index < UserBooter->MmioWhitelist.Count; ++Index) { + Comment = OC_BLOB_GET (&UserBooter->MmioWhitelist.Values[Index]->Comment); + IsMmioWhitelistEnabled = UserBooter->MmioWhitelist.Values[Index]->Enabled; + + // + // Sanitise strings. + // + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "Booter->MmioWhitelist[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + + if (IsMmioWhitelistEnabled) { + ShouldEnableDevirtualiseMmio = TRUE; + } + } + + for (Index = 0; Index < UserBooter->Patch.Count; ++Index) { + Comment = OC_BLOB_GET (&UserBooter->Patch.Values[Index]->Comment); + Arch = OC_BLOB_GET (&UserBooter->Patch.Values[Index]->Arch); + Identifier = OC_BLOB_GET (&UserBooter->Patch.Values[Index]->Identifier); + Find = OC_BLOB_GET (&UserBooter->Patch.Values[Index]->Find); + FindSize = UserBooter->Patch.Values[Index]->Find.Size; + Replace = OC_BLOB_GET (&UserBooter->Patch.Values[Index]->Replace); + ReplaceSize = UserBooter->Patch.Values[Index]->Replace.Size; + Mask = OC_BLOB_GET (&UserBooter->Patch.Values[Index]->Mask); + MaskSize = UserBooter->Patch.Values[Index]->Mask.Size; + ReplaceMask = OC_BLOB_GET (&UserBooter->Patch.Values[Index]->ReplaceMask); + ReplaceMaskSize = UserBooter->Patch.Values[Index]->ReplaceMask.Size; + + // + // Sanitise strings. + // + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "Booter->Patch[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + if (!AsciiArchIsLegal (Arch, FALSE)) { + DEBUG ((DEBUG_WARN, "Booter->Patch[%u]->Arch is borked (Can only be Any, i386, and x86_64)!\n", Index)); + ++ErrorCount; + } + if (!AsciiIdentifierIsLegal (Identifier, FALSE)) { + DEBUG ((DEBUG_WARN, "Booter->Patch[%u]->Identifier contains illegal character!\n", Index)); + ++ErrorCount; + } + + // + // Checks for size. + // + ErrorCount += ValidatePatch ( + "Booter->Patch", + Index, + FALSE, + Find, + FindSize, + Replace, + ReplaceSize, + Mask, + MaskSize, + ReplaceMask, + ReplaceMaskSize + ); + } + + for (Index = 0; Index < UserUefi->Drivers.Count; ++Index) { + Driver = OC_BLOB_GET (UserUefi->Drivers.Values[Index]); + + // + // Skip sanitising UEFI->Drivers as it will be performed when checking UEFI section. + // + + if (AsciiStrCmp (Driver, "OpenRuntime.efi") == 0) { + HasOpenRuntimeEfiDriver = TRUE; + } + } + + if (ShouldEnableDevirtualiseMmio && !IsDevirtualiseMmioEnabled) { + DEBUG ((DEBUG_WARN, "There are enabled entries under Booter->MmioWhitelist, but DevirtualiseMmio is not enabled!\n")); + ++ErrorCount; + } + if (!HasOpenRuntimeEfiDriver) { + if (IsProvideCustomSlideEnabled) { + DEBUG ((DEBUG_WARN, "Booter->Quirks->ProvideCustomSlide is enabled, but OpenRuntime.efi is not loaded at UEFI->Drivers!\n")); + ++ErrorCount; + } + if (IsDisableVariableWriteEnabled) { + DEBUG ((DEBUG_WARN, "Booter->Quirks->DisableVariableWrite is enabled, but OpenRuntime.efi is not loaded at UEFI->Drivers!\n")); + ++ErrorCount; + } + if (IsEnableWriteUnprotectorEnabled) { + DEBUG ((DEBUG_WARN, "Booter->Quirks->EnableWriteUnprotector is enabled, but OpenRuntime.efi is not loaded at UEFI->Drivers!\n")); + ++ErrorCount; + } + } + if (!IsProvideCustomSlideEnabled) { + if (IsAllowRelocationBlockEnabled) { + DEBUG ((DEBUG_WARN, "Booter->Quirks->AllowRelocationBlock is enabled, but ProvideCustomSlide is not enabled altogether!\n")); + ++ErrorCount; + } + if (IsEnableSafeModeSlideEnabled) { + DEBUG ((DEBUG_WARN, "Booter->Quirks->EnableSafeModeSlide is enabled, but ProvideCustomSlide is not enabled altogether!\n")); + ++ErrorCount; + } + if (MaxSlide > 0) { + DEBUG ((DEBUG_WARN, "Booter->Quirks->ProvideMaxSlide is set to %u, but ProvideCustomSlide is not enabled altogether!\n", MaxSlide)); + ++ErrorCount; + } + } + + return ReportError (__func__, ErrorCount); +} diff --git a/Utilities/ocvalidate/ValidateDeviceProperties.c b/Utilities/ocvalidate/ValidateDeviceProperties.c new file mode 100644 index 00000000..e555ed99 --- /dev/null +++ b/Utilities/ocvalidate/ValidateDeviceProperties.c @@ -0,0 +1,91 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +UINT32 +CheckDeviceProperties ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + UINT32 DeviceIndex; + UINT32 PropertyIndex; + OC_DEV_PROP_CONFIG *UserDevProp; + CONST CHAR8 *AsciiDevicePath; + CONST CHAR8 *AsciiProperty; + OC_ASSOC *PropertyMap; + + DEBUG ((DEBUG_VERBOSE, "config loaded into DeviceProperties checker!\n")); + + ErrorCount = 0; + UserDevProp = &Config->DeviceProperties; + + for (DeviceIndex = 0; DeviceIndex < UserDevProp->Delete.Count; ++DeviceIndex) { + AsciiDevicePath = OC_BLOB_GET (UserDevProp->Delete.Keys[DeviceIndex]); + + if (!AsciiDevicePathIsLegal (AsciiDevicePath)) { + DEBUG ((DEBUG_WARN, "DeviceProperties->Delete[%u]->DevicePath is borked! Please check the information above!\n", DeviceIndex)); + ++ErrorCount; + } + + for (PropertyIndex = 0; PropertyIndex < UserDevProp->Delete.Values[DeviceIndex]->Count; ++PropertyIndex) { + AsciiProperty = OC_BLOB_GET (UserDevProp->Delete.Values[DeviceIndex]->Values[PropertyIndex]); + + // + // Sanitise strings. + // + if (!AsciiPropertyIsLegal (AsciiProperty)) { + DEBUG (( + DEBUG_WARN, + "DeviceProperties->Delete[%u]->Property[%u] contains illegal character!\n", + DeviceIndex, + PropertyIndex + )); + ++ErrorCount; + } + } + } + + for (DeviceIndex = 0; DeviceIndex < UserDevProp->Add.Count; ++DeviceIndex) { + PropertyMap = UserDevProp->Add.Values[DeviceIndex]; + AsciiDevicePath = OC_BLOB_GET (UserDevProp->Add.Keys[DeviceIndex]); + + if (!AsciiDevicePathIsLegal (AsciiDevicePath)) { + DEBUG ((DEBUG_WARN, "DeviceProperties->Add[%u]->DevicePath is borked! Please check the information above!\n", DeviceIndex)); + ++ErrorCount; + } + + for (PropertyIndex = 0; PropertyIndex < PropertyMap->Count; ++PropertyIndex) { + AsciiProperty = OC_BLOB_GET (PropertyMap->Keys[PropertyIndex]); + + // + // Sanitise strings. + // + if (!AsciiPropertyIsLegal (AsciiProperty)) { + DEBUG (( + DEBUG_WARN, + "DeviceProperties->Add[%u]->Property[%u] contains illegal character!\n", + DeviceIndex, + PropertyIndex + )); + ++ErrorCount; + } + } + } + + return ReportError (__func__, ErrorCount); +} diff --git a/Utilities/ocvalidate/ValidateKernel.c b/Utilities/ocvalidate/ValidateKernel.c new file mode 100644 index 00000000..3668ecb3 --- /dev/null +++ b/Utilities/ocvalidate/ValidateKernel.c @@ -0,0 +1,440 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +#include + +#define INDEX_KEXT_LILU 0U + +typedef struct KEXT_PRECEDENCE_ { + CONST CHAR8 *Child; + CONST CHAR8 *Parent; +} KEXT_PRECEDENCE; + +typedef struct KEXT_INFO_ { + CONST CHAR8 *KextBundlePath; + CONST CHAR8 *KextExecutablePath; + CONST CHAR8 *KextPlistPath; +} KEXT_INFO; + +STATIC KEXT_PRECEDENCE mKextPrecedence[] = { + { "VirtualSMC.kext", "Lilu.kext" }, + { "WhateverGreen.kext", "Lilu.kext" }, + // + // TODO: Add more kexts here... + // +}; +STATIC UINTN mKextPrecedenceSize = ARRAY_SIZE (mKextPrecedence); + +STATIC KEXT_INFO mKextInfo[] = { + // + // NOTE: Index of Lilu should always be 0. Please add entries after this if necessary. + // + { "Lilu.kext", "Contents/MacOS/Lilu", "Contents/Info.plist" }, + { "VirtualSMC.kext", "Contents/MacOS/VirtualSMC", "Contents/Info.plist" }, + { "WhateverGreen.kext", "Contents/MacOS/WhateverGreen", "Contents/Info.plist" }, + // + // TODO: Add more kexts here... + // +}; +STATIC UINTN mKextInfoSize = ARRAY_SIZE (mKextInfo); + +UINT32 +CheckKernel ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + UINT32 Index; + OC_KERNEL_CONFIG *UserKernel; + OC_PLATFORM_CONFIG *UserPlatformInfo; + CONST CHAR8 *Arch; + CONST CHAR8 *BundlePath; + CONST CHAR8 *Comment; + CONST CHAR8 *ExecutablePath; + CONST CHAR8 *MaxKernel; + CONST CHAR8 *MinKernel; + CONST CHAR8 *PlistPath; + CONST CHAR8 *Identifier; + BOOLEAN IsDisableLinkeditJettisonEnabled; + BOOLEAN IsCustomSMBIOSGuidEnabled; + CONST CHAR8 *UpdateSMBIOSMode; + CONST CHAR8 *Base; + CONST UINT8 *Find; + UINT32 FindSize; + CONST UINT8 *Replace; + UINT32 ReplaceSize; + CONST UINT8 *Mask; + UINT32 MaskSize; + CONST UINT8 *ReplaceMask; + UINT32 ReplaceMaskSize; + CONST CHAR8 *KernelCache; + UINTN IndexKextInfo; + UINTN IndexKextPrecedence; + BOOLEAN HasParent; + CONST CHAR8 *CurrentKext; + CONST CHAR8 *ParentKext; + CONST CHAR8 *ChildKext; + + DEBUG ((DEBUG_VERBOSE, "config loaded into Kernel checker!\n")); + // + // Ensure Lilu to be always placed where it is supposed to be. + // + ASSERT (AsciiStrCmp (mKextInfo[INDEX_KEXT_LILU].KextBundlePath, "Lilu.kext") == 0); + ASSERT (AsciiStrCmp (mKextInfo[INDEX_KEXT_LILU].KextExecutablePath, "Contents/MacOS/Lilu") == 0); + ASSERT (AsciiStrCmp (mKextInfo[INDEX_KEXT_LILU].KextPlistPath, "Contents/Info.plist") == 0); + + ErrorCount = 0; + UserKernel = &Config->Kernel; + UserPlatformInfo = &Config->PlatformInfo; + IsDisableLinkeditJettisonEnabled = UserKernel->Quirks.DisableLinkeditJettison; + IsCustomSMBIOSGuidEnabled = UserKernel->Quirks.CustomSmbiosGuid; + UpdateSMBIOSMode = OC_BLOB_GET (&UserPlatformInfo->UpdateSmbiosMode); + KernelCache = OC_BLOB_GET (&UserKernel->Scheme.KernelCache); + + for (Index = 0; Index < UserKernel->Add.Count; ++Index) { + Arch = OC_BLOB_GET (&UserKernel->Add.Values[Index]->Arch); + BundlePath = OC_BLOB_GET (&UserKernel->Add.Values[Index]->BundlePath); + Comment = OC_BLOB_GET (&UserKernel->Add.Values[Index]->Comment); + ExecutablePath = OC_BLOB_GET (&UserKernel->Add.Values[Index]->ExecutablePath); + MaxKernel = OC_BLOB_GET (&UserKernel->Add.Values[Index]->MaxKernel); + MinKernel = OC_BLOB_GET (&UserKernel->Add.Values[Index]->MinKernel); + PlistPath = OC_BLOB_GET (&UserKernel->Add.Values[Index]->PlistPath); + + // + // Sanitise strings. + // + if (!AsciiArchIsLegal (Arch, FALSE)) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->Arch is borked (Can only be Any, i386, and x86_64)!\n", Index)); + ++ErrorCount; + } + if (!AsciiFileSystemPathIsLegal (BundlePath)) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->BundlePath contains illegal character!\n", Index)); + ++ErrorCount; + continue; + } + // + // Valid BundlePath must contain .kext suffix. + // + if (!OcAsciiEndsWith (BundlePath, ".kext", TRUE)) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->BundlePath does NOT contain .kext suffix!\n", Index)); + ++ErrorCount; + } + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + if (!AsciiFileSystemPathIsLegal (ExecutablePath)) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->ExecutablePath contains illegal character!\n", Index)); + ++ErrorCount; + continue; + } + if (!AsciiFileSystemPathIsLegal (PlistPath)) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->PlistPath contains illegal character!\n", Index)); + ++ErrorCount; + continue; + } + // + // Valid PlistPath must contain .plist suffix. + // + if (!OcAsciiEndsWith (PlistPath, ".plist", TRUE)) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->PlistPath does NOT contain .plist suffix!\n", Index)); + ++ErrorCount; + } + + // + // FIXME: Handle correct kernel version checking. + // + if (MaxKernel[0] != '\0' && OcParseDarwinVersion (MaxKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->MaxKernel (currently set to %a) is borked!\n", Index, MaxKernel)); + ++ErrorCount; + } + if (MinKernel[0] != '\0' && OcParseDarwinVersion (MinKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u]->MinKernel (currently set to %a) is borked!\n", Index, MinKernel)); + ++ErrorCount; + } + + for (IndexKextInfo = 0; IndexKextInfo < mKextInfoSize; ++IndexKextInfo) { + if (AsciiStrCmp (BundlePath, mKextInfo[IndexKextInfo].KextBundlePath) == 0) { + // + // BundlePath matched. Continue checking ExecutablePath and PlistPath. + // + if (AsciiStrCmp (ExecutablePath, mKextInfo[IndexKextInfo].KextExecutablePath) == 0 + && AsciiStrCmp (PlistPath, mKextInfo[IndexKextInfo].KextPlistPath) == 0) { + // + // Special check for Lilu and Quirks->DisableLinkeditJettison. + // + if (IndexKextInfo == INDEX_KEXT_LILU) { + if (!IsDisableLinkeditJettisonEnabled) { + DEBUG ((DEBUG_WARN, "Lilu.kext is loaded at Kernel->Add[%u], but DisableLinkeditJettison is not enabled at Kernel->Quirks!\n", Index)); + ++ErrorCount; + } + } + } else { + DEBUG (( + DEBUG_WARN, + "Kernel->Add[%u] discovers %a, but its ExecutablePath (%a) or PlistPath (%a) is borked!\n", + IndexKextInfo, + BundlePath, + ExecutablePath, + PlistPath + )); + ++ErrorCount; + } + } + } + } + + // + // Special checks for kext precedence from Acidanthera. + // + for (IndexKextPrecedence = 0; IndexKextPrecedence < mKextPrecedenceSize; ++IndexKextPrecedence) { + HasParent = FALSE; + + for (Index = 0; Index < UserKernel->Add.Count; ++Index) { + CurrentKext = OC_BLOB_GET (&UserKernel->Add.Values[Index]->BundlePath); + ParentKext = mKextPrecedence[IndexKextPrecedence].Parent; + ChildKext = mKextPrecedence[IndexKextPrecedence].Child; + + if (AsciiStrCmp (CurrentKext, ParentKext) == 0) { + HasParent = TRUE; + } else if (AsciiStrCmp (CurrentKext, ChildKext) == 0) { + if (!HasParent) { + DEBUG ((DEBUG_WARN, "Kernel->Add[%u] discovers %a, but its Parent (%a) is either placed after it or is missing!\n", Index, CurrentKext, ParentKext)); + ++ErrorCount; + } + // + // Parent is already found before Child. Done. + // + break; + } + } + } + + for (Index = 0; Index < UserKernel->Block.Count; ++Index) { + Arch = OC_BLOB_GET (&UserKernel->Block.Values[Index]->Arch); + Comment = OC_BLOB_GET (&UserKernel->Block.Values[Index]->Comment); + Identifier = OC_BLOB_GET (&UserKernel->Block.Values[Index]->Identifier); + MaxKernel = OC_BLOB_GET (&UserKernel->Block.Values[Index]->MaxKernel); + MinKernel = OC_BLOB_GET (&UserKernel->Block.Values[Index]->MinKernel); + + // + // Sanitise strings. + // + if (!AsciiArchIsLegal (Arch, FALSE)) { + DEBUG ((DEBUG_WARN, "Kernel->Block[%u]->Arch is borked (Can only be Any, i386, and x86_64)!\n", Index)); + ++ErrorCount; + } + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "Kernel->Block[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + if (!AsciiIdentifierIsLegal (Identifier, TRUE)) { + DEBUG ((DEBUG_WARN, "Kernel->Block[%u]->Identifier contains illegal character!\n", Index)); + ++ErrorCount; + } + + // + // FIXME: Handle correct kernel version checking. + // + if (MaxKernel[0] != '\0' && OcParseDarwinVersion (MaxKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Block[%u]->MaxKernel (currently set to %a) is borked!\n", Index, MaxKernel)); + ++ErrorCount; + } + if (MinKernel[0] != '\0' && OcParseDarwinVersion (MinKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Block[%u]->MinKernel (currently set to %a) is borked!\n", Index, MinKernel)); + ++ErrorCount; + } + } + + // + // FIXME: Handle correct kernel version checking. + // + MaxKernel = OC_BLOB_GET (&UserKernel->Emulate.MaxKernel); + MinKernel = OC_BLOB_GET (&UserKernel->Emulate.MinKernel); + if (MaxKernel[0] != '\0' && OcParseDarwinVersion (MaxKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Emulate->MaxKernel (currently set to %a) is borked!\n", MaxKernel)); + ++ErrorCount; + } + if (MinKernel[0] != '\0' && OcParseDarwinVersion (MinKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Emulate->MinKernel (currently set to %a) is borked!\n", MinKernel)); + ++ErrorCount; + } + + if (!DataHasProperMasking (UserKernel->Emulate.Cpuid1Data, UserKernel->Emulate.Cpuid1Mask, sizeof (UserKernel->Emulate.Cpuid1Data))) { + DEBUG ((DEBUG_WARN, "Kernel->Emulate->Cpuid1Data requires Cpuid1Mask to be active for replaced bits!\n")); + ++ErrorCount; + } + + for (Index = 0; Index < UserKernel->Force.Count; ++Index) { + Arch = OC_BLOB_GET (&UserKernel->Force.Values[Index]->Arch); + BundlePath = OC_BLOB_GET (&UserKernel->Force.Values[Index]->BundlePath); + Comment = OC_BLOB_GET (&UserKernel->Force.Values[Index]->Comment); + ExecutablePath = OC_BLOB_GET (&UserKernel->Force.Values[Index]->ExecutablePath); + Identifier = OC_BLOB_GET (&UserKernel->Force.Values[Index]->Identifier); + MaxKernel = OC_BLOB_GET (&UserKernel->Force.Values[Index]->MaxKernel); + MinKernel = OC_BLOB_GET (&UserKernel->Force.Values[Index]->MinKernel); + PlistPath = OC_BLOB_GET (&UserKernel->Force.Values[Index]->PlistPath); + + // + // Sanitise strings. + // + if (!AsciiArchIsLegal (Arch, FALSE)) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->Arch is borked (Can only be Any, i386, and x86_64)!\n", Index)); + ++ErrorCount; + } + if (!AsciiIdentifierIsLegal (Identifier, TRUE)) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->Identifier contains illegal character!\n", Index)); + ++ErrorCount; + } + if (!AsciiFileSystemPathIsLegal (BundlePath)) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->BundlePath contains illegal character!\n", Index)); + ++ErrorCount; + continue; + } + // + // Valid BundlePath must contain .kext suffix. + // + if (!OcAsciiEndsWith (BundlePath, ".kext", TRUE)) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->BundlePath does NOT contain .kext suffix!\n", Index)); + ++ErrorCount; + } + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + if (!AsciiFileSystemPathIsLegal (ExecutablePath)) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->ExecutablePath contains illegal character!\n", Index)); + ++ErrorCount; + continue; + } + if (!AsciiFileSystemPathIsLegal (PlistPath)) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->PlistPath contains illegal character!\n", Index)); + ++ErrorCount; + continue; + } + // + // Valid PlistPath must contain .plist suffix. + // + if (!OcAsciiEndsWith (PlistPath, ".plist", TRUE)) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->PlistPath does NOT contain .plist suffix!\n", Index)); + ++ErrorCount; + } + + // + // FIXME: Handle correct kernel version checking. + // + if (MaxKernel[0] != '\0' && OcParseDarwinVersion (MaxKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->MaxKernel (currently set to %a) is borked!\n", Index, MaxKernel)); + ++ErrorCount; + } + if (MinKernel[0] != '\0' && OcParseDarwinVersion (MinKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Force[%u]->MinKernel (currently set to %a) is borked!\n", Index, MinKernel)); + ++ErrorCount; + } + + } + + for (Index = 0; Index < UserKernel->Patch.Count; ++Index) { + Base = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->Base); + Comment = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->Comment); + Arch = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->Arch); + Identifier = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->Identifier); + Find = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->Find); + FindSize = UserKernel->Patch.Values[Index]->Find.Size; + Replace = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->Replace); + ReplaceSize = UserKernel->Patch.Values[Index]->Replace.Size; + Mask = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->Mask); + MaskSize = UserKernel->Patch.Values[Index]->Mask.Size; + ReplaceMask = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->ReplaceMask); + ReplaceMaskSize = UserKernel->Patch.Values[Index]->ReplaceMask.Size; + MaxKernel = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->MaxKernel); + MinKernel = OC_BLOB_GET (&UserKernel->Patch.Values[Index]->MinKernel); + + // + // Sanitise strings. + // + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "Kernel->Patch[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + if (!AsciiArchIsLegal (Arch, FALSE)) { + DEBUG ((DEBUG_WARN, "Kernel->Patch[%u]->Arch is borked (Can only be Any, i386, and x86_64)!\n", Index)); + ++ErrorCount; + } + if (!AsciiIdentifierIsLegal (Identifier, TRUE)) { + DEBUG ((DEBUG_WARN, "Kernel->Patch[%u]->Identifier contains illegal character!\n", Index)); + ++ErrorCount; + } + + // + // FIXME: Handle correct kernel version checking. + // + if (MaxKernel[0] != '\0' && OcParseDarwinVersion (MaxKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Patch[%u]->MaxKernel (currently set to %a) is borked!\n", Index, MaxKernel)); + ++ErrorCount; + } + if (MinKernel[0] != '\0' && OcParseDarwinVersion (MinKernel) == 0) { + DEBUG ((DEBUG_WARN, "Kernel->Patch[%u]->MinKernel (currently set to %a) is borked!\n", Index, MinKernel)); + ++ErrorCount; + } + + // + // Checks for size. + // + ErrorCount += ValidatePatch ( + "Kernel->Patch", + Index, + Base[0] != '\0' && FindSize == 0, + Find, + FindSize, + Replace, + ReplaceSize, + Mask, + MaskSize, + ReplaceMask, + ReplaceMaskSize + ); + } + + // + // Sanitise Kernel->Scheme keys. + // + Arch = OC_BLOB_GET (&UserKernel->Scheme.KernelArch); + if (!AsciiArchIsLegal (Arch, TRUE)) { + DEBUG ((DEBUG_WARN, "Kernel->Scheme->KernelArch is borked (Can only be Auto, i386, i386-user32, or x86_64)!\n")); + ++ErrorCount; + } + + if (AsciiStrCmp (KernelCache, "Auto") != 0 + && AsciiStrCmp (KernelCache, "Cacheless") != 0 + && AsciiStrCmp (KernelCache, "Mkext") != 0 + && AsciiStrCmp (KernelCache, "Prelinked") != 0) { + DEBUG ((DEBUG_WARN, "Kernel->Scheme->KernelCache is borked (Can only be Auto, Cacheless, Mkext, or Prelinked)!\n")); + ++ErrorCount; + } + + // + // CustomSMBIOSGuid quirk requires UpdateSMBIOSMode at PlatformInfo set to Custom. + // + if (IsCustomSMBIOSGuidEnabled && AsciiStrCmp (UpdateSMBIOSMode, "Custom") != 0) { + DEBUG ((DEBUG_WARN, "Kernel->Quirks->CustomSMBIOSGuid is enabled, but PlatformInfo->UpdateSMBIOSMode is not set to Custom!\n")); + ++ErrorCount; + } + + return ReportError (__func__, ErrorCount); +} diff --git a/Utilities/ocvalidate/ValidateMisc.c b/Utilities/ocvalidate/ValidateMisc.c new file mode 100644 index 00000000..712b6816 --- /dev/null +++ b/Utilities/ocvalidate/ValidateMisc.c @@ -0,0 +1,262 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +#include +#include +#include + +/** + Validate if SecureBootModel has allowed value. + + @param[in] SecureBootModel SecureBootModel retrieved from user config. + + @retval TRUE If SecureBootModel is valid. +**/ +STATIC +BOOLEAN +ValidateSecureBootModel ( + IN CONST CHAR8 *SecureBootModel + ) +{ + UINT32 Index; + CONST CHAR8 *AllowedSecureBootModel[] = { + "Default", "Disabled", + "j137", "j680", "j132", "j174", "j140k", + "j780", "j213", "j140a", "j152f", "j160", + "j230k", "j214k", "j223", "j215", "j185", "j185f", + "x86legacy" + }; + + for (Index = 0; Index < ARRAY_SIZE (AllowedSecureBootModel); ++Index) { + if (AsciiStrCmp (SecureBootModel, AllowedSecureBootModel[Index]) == 0) { + return TRUE; + } + } + + return FALSE; +} + +UINT32 +CheckMisc ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + UINT32 Index; + OC_MISC_CONFIG *UserMisc; + OC_UEFI_CONFIG *UserUefi; + UINT32 ConsoleAttributes; + CONST CHAR8 *HibernateMode; + UINT32 PickerAttributes; + CONST CHAR8 *PickerMode; + UINT64 DisplayLevel; + UINT64 AllowedDisplayLevel; + UINT64 HaltLevel; + UINT64 AllowedHaltLevel; + UINT32 Target; + CONST CHAR8 *BootProtect; + BOOLEAN IsRequestBootVarRoutingEnabled; + CONST CHAR8 *AsciiDmgLoading; + UINT32 ExposeSensitiveData; + CONST CHAR8 *AsciiVault; + UINT32 ScanPolicy; + UINT32 AllowedScanPolicy; + CONST CHAR8 *SecureBootModel; + CONST CHAR8 *Arguments; + CONST CHAR8 *Comment; + CONST CHAR8 *AsciiName; + CONST CHAR16 *UnicodeName; + CONST CHAR8 *Path; + + DEBUG ((DEBUG_VERBOSE, "config loaded into Misc checker!\n")); + + ErrorCount = 0; + UserMisc = &Config->Misc; + UserUefi = &Config->Uefi; + ConsoleAttributes = UserMisc->Boot.ConsoleAttributes; + HibernateMode = OC_BLOB_GET (&UserMisc->Boot.HibernateMode); + PickerAttributes = UserMisc->Boot.PickerAttributes; + PickerMode = OC_BLOB_GET (&UserMisc->Boot.PickerMode); + DisplayLevel = UserMisc->Debug.DisplayLevel; + AllowedDisplayLevel = DEBUG_WARN | DEBUG_INFO | DEBUG_VERBOSE | DEBUG_ERROR; + HaltLevel = DisplayLevel; + AllowedHaltLevel = AllowedDisplayLevel; + Target = UserMisc->Debug.Target; + BootProtect = OC_BLOB_GET (&UserMisc->Security.BootProtect); + IsRequestBootVarRoutingEnabled = UserUefi->Quirks.RequestBootVarRouting; + AsciiDmgLoading = OC_BLOB_GET (&UserMisc->Security.DmgLoading); + ExposeSensitiveData = UserMisc->Security.ExposeSensitiveData; + AsciiVault = OC_BLOB_GET (&UserMisc->Security.Vault); + ScanPolicy = UserMisc->Security.ScanPolicy; + AllowedScanPolicy = OC_SCAN_FILE_SYSTEM_LOCK | OC_SCAN_DEVICE_LOCK | OC_SCAN_DEVICE_BITS | OC_SCAN_FILE_SYSTEM_BITS; + SecureBootModel = OC_BLOB_GET (&UserMisc->Security.SecureBootModel); + + if ((ConsoleAttributes & ~0x7FU) != 0) { + DEBUG ((DEBUG_WARN, "Misc->Boot->ConsoleAttributes has unknown bits set!\n")); + ++ErrorCount; + } + + if (AsciiStrCmp (HibernateMode, "None") != 0 + && AsciiStrCmp (HibernateMode, "Auto") != 0 + && AsciiStrCmp (HibernateMode, "RTC") != 0 + && AsciiStrCmp (HibernateMode, "NVRAM") != 0) { + DEBUG ((DEBUG_WARN, "Misc->Boot->HibernateMode is borked (Can only be None, Auto, RTC, or NVRAM)!\n")); + ++ErrorCount; + } + + if ((PickerAttributes & ~OC_ATTR_ALL_BITS) != 0) { + DEBUG ((DEBUG_WARN, "Misc->Boot->PickerAttributes is has unknown bits set!\n")); + ++ErrorCount; + } + + // + // FIXME: Is OpenCanopy.efi mandatory if set to External? Or is this just a suggestion? + // + if (AsciiStrCmp (PickerMode, "Builtin") != 0 + && AsciiStrCmp (PickerMode, "External") != 0 + && AsciiStrCmp (PickerMode, "Apple") != 0) { + DEBUG ((DEBUG_WARN, "Misc->Boot->PickerMode is borked (Can only be Builtin, External, or Apple)!\n")); + ++ErrorCount; + } + + // + // FIXME: Check whether DisplayLevel only supports values within AllowedDisplayLevel, or all possible levels in DebugLib.h? + // + if ((DisplayLevel & ~AllowedDisplayLevel) != 0) { + DEBUG ((DEBUG_WARN, "Misc->Debug->DisplayLevel is has unknown bits set!\n")); + ++ErrorCount; + } + if ((HaltLevel & ~AllowedHaltLevel) != 0) { + DEBUG ((DEBUG_WARN, "Misc->Security->HaltLevel has unknown bits set!\n")); + ++ErrorCount; + } + + if ((Target & ~OC_LOG_ALL_BITS) != 0) { + DEBUG ((DEBUG_WARN, "Misc->Debug->Target has unknown bits set!\n")); + ++ErrorCount; + } + + // + // TODO: Check requirements of Security->AuthRestart. + // + + if (AsciiStrCmp (BootProtect, "None") != 0 + && AsciiStrCmp (BootProtect, "Bootstrap") != 0 + && AsciiStrCmp (BootProtect, "BootstrapShort") != 0) { + DEBUG ((DEBUG_WARN, "Misc->Security->BootProtect is borked (Can only be None, Bootstrap, or BootstrapShort)!\n")); + ++ErrorCount; + } else if (AsciiStrCmp (BootProtect, "Bootstrap") == 0 + || AsciiStrCmp (BootProtect, "BootstrapShort") == 0) { + if (!IsRequestBootVarRoutingEnabled) { + DEBUG ((DEBUG_WARN, "Misc->Security->BootProtect is set to %a which requires UEFI->Quirks->RequestBootVarRouting to be enabled!\n", BootProtect)); + ++ErrorCount; + } + // + // NOTE: RequestBootVarRouting requires OpenRuntime.efi, which will be checked in UEFI checker. + // + } + + if (AsciiStrCmp (AsciiDmgLoading, "Disabled") != 0 + && AsciiStrCmp (AsciiDmgLoading, "Signed") != 0 + && AsciiStrCmp (AsciiDmgLoading, "Any") != 0) { + DEBUG ((DEBUG_WARN, "Misc->Security->DmgLoading is borked (Can only be Disabled, Signed, or Any)!\n")); + ++ErrorCount; + } + + if ((ExposeSensitiveData & ~OCS_EXPOSE_ALL_BITS) != 0) { + DEBUG ((DEBUG_WARN, "Misc->Security->ExposeSensitiveData has unknown bits set!\n")); + ++ErrorCount; + } + + if (AsciiStrCmp (AsciiVault, "Optional") != 0 + && AsciiStrCmp (AsciiVault, "Basic") != 0 + && AsciiStrCmp (AsciiVault, "Secure") != 0) { + DEBUG ((DEBUG_WARN, "Misc->Security->Vault is borked (Can only be Optional, Basic, or Secure)!\n")); + ++ErrorCount; + } + + // + // ScanPolicy can be zero (failsafe value). + // + if (ScanPolicy != 0) { + if ((ScanPolicy & ~AllowedScanPolicy) != 0) { + DEBUG ((DEBUG_WARN, "Misc->Security->ScanPolicy has unknown bits set!\n")); + ++ErrorCount; + } + + if ((ScanPolicy & OC_SCAN_FILE_SYSTEM_BITS) != 0 && (ScanPolicy & OC_SCAN_FILE_SYSTEM_LOCK) == 0) { + DEBUG ((DEBUG_WARN, "Misc->Security->ScanPolicy requests scanning filesystem, but OC_SCAN_FILE_SYSTEM_LOCK (bit 0) is not set!\n")); + ++ErrorCount; + } + + if ((ScanPolicy & OC_SCAN_DEVICE_BITS) != 0 && (ScanPolicy & OC_SCAN_DEVICE_LOCK) == 0) { + DEBUG ((DEBUG_WARN, "Misc->Security->ScanPolicy requests scanning devices, but OC_SCAN_DEVICE_LOCK (bit 1) is not set!\n")); + ++ErrorCount; + } + } + + // + // Validate SecureBootModel. + // + if (!ValidateSecureBootModel (SecureBootModel)) { + DEBUG ((DEBUG_WARN, "Misc->Security->SecureBootModel is borked!\n")); + ++ErrorCount; + } + + for (Index = 0; Index < UserMisc->Entries.Count; ++Index) { + Arguments = OC_BLOB_GET (&UserMisc->Entries.Values[Index]->Arguments); + Comment = OC_BLOB_GET (&UserMisc->Entries.Values[Index]->Comment); + AsciiName = OC_BLOB_GET (&UserMisc->Entries.Values[Index]->Name); + Path = OC_BLOB_GET (&UserMisc->Entries.Values[Index]->Path); + + // + // Sanitise strings. + // + // NOTE: As Arguments takes identical requirements of Comment, + // we use Comment sanitiser here. + // + if (!AsciiCommentIsLegal (Arguments)) { + DEBUG ((DEBUG_WARN, "Misc->Entries[%u]->Arguments contains illegal character!\n", Index)); + ++ErrorCount; + } + if (!AsciiCommentIsLegal (Comment)) { + DEBUG ((DEBUG_WARN, "Misc->Entries[%u]->Comment contains illegal character!\n", Index)); + ++ErrorCount; + } + + UnicodeName = AsciiStrCopyToUnicode (AsciiName, 0); + if (UnicodeName != NULL) { + if (!UnicodeIsFilteredString (UnicodeName, TRUE)) { + DEBUG ((DEBUG_WARN, "Misc->Entries[%u]->Name contains illegal character!\n", Index)); + ++ErrorCount; + } + + FreePool ((VOID *) UnicodeName); + } + + // + // FIXME: Properly sanitise Path. + // + if (!AsciiCommentIsLegal (Path)) { + DEBUG ((DEBUG_WARN, "Misc->Entries[%u]->Path contains illegal character!\n", Index)); + ++ErrorCount; + } + } + + return ReportError (__func__, ErrorCount); +} diff --git a/Utilities/ocvalidate/ValidateNVRAM.c b/Utilities/ocvalidate/ValidateNVRAM.c new file mode 100644 index 00000000..2797a6ba --- /dev/null +++ b/Utilities/ocvalidate/ValidateNVRAM.c @@ -0,0 +1,123 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +UINT32 +CheckNVRAM ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + EFI_STATUS Status; + UINT32 GuidIndex; + UINT32 VariableIndex; + OC_NVRAM_CONFIG *UserNVRAM; + CONST CHAR8 *AsciiGuid; + CONST CHAR8 *AsciiNVRAMKey; + GUID VariableGuid; + OC_ASSOC *VariableMap; + + DEBUG ((DEBUG_VERBOSE, "config loaded into NVRAM checker!\n")); + + ErrorCount = 0; + UserNVRAM = &Config->Nvram; + + for (GuidIndex = 0; GuidIndex < UserNVRAM->Add.Count; ++GuidIndex) { + AsciiGuid = OC_BLOB_GET (UserNVRAM->Add.Keys[GuidIndex]); + Status = AsciiStrToGuid (AsciiGuid, &VariableGuid); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "NVRAM->Add[%u] has borked GUID!\n", GuidIndex)); + ++ErrorCount; + } + + VariableMap = UserNVRAM->Add.Values[GuidIndex]; + + for (VariableIndex = 0; VariableIndex < VariableMap->Count; ++VariableIndex) { + AsciiNVRAMKey = OC_BLOB_GET (VariableMap->Keys[VariableIndex]); + + // + // Sanitise strings. + // + if (!AsciiPropertyIsLegal (AsciiNVRAMKey)) { + DEBUG (( + DEBUG_WARN, + "NVRAM->Add[%u]->Key[%u] contains illegal character!\n", + GuidIndex, + VariableIndex + )); + ++ErrorCount; + } + } + } + + for (GuidIndex = 0; GuidIndex < UserNVRAM->Delete.Count; ++GuidIndex) { + AsciiGuid = OC_BLOB_GET (UserNVRAM->Delete.Keys[GuidIndex]); + Status = AsciiStrToGuid (AsciiGuid, &VariableGuid); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "NVRAM->Delete[%u] has borked GUID!\n", GuidIndex)); + ++ErrorCount; + } + + for (VariableIndex = 0; VariableIndex < UserNVRAM->Delete.Values[GuidIndex]->Count; ++VariableIndex) { + AsciiNVRAMKey = OC_BLOB_GET (UserNVRAM->Delete.Values[GuidIndex]->Values[VariableIndex]); + + // + // Sanitise strings. + // + if (!AsciiPropertyIsLegal (AsciiNVRAMKey)) { + DEBUG (( + DEBUG_WARN, + "NVRAM->Delete[%u]->Key[%u] contains illegal character!\n", + GuidIndex, + VariableIndex + )); + ++ErrorCount; + } + } + } + + for (GuidIndex = 0; GuidIndex < UserNVRAM->Legacy.Count; ++GuidIndex) { + AsciiGuid = OC_BLOB_GET (UserNVRAM->Legacy.Keys[GuidIndex]); + Status = AsciiStrToGuid (AsciiGuid, &VariableGuid); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "NVRAM->LegacySchema[%u] has borked GUID!\n", GuidIndex)); + ++ErrorCount; + } + + for (VariableIndex = 0; VariableIndex < UserNVRAM->Legacy.Values[GuidIndex]->Count; ++VariableIndex) { + AsciiNVRAMKey = OC_BLOB_GET (UserNVRAM->Legacy.Values[GuidIndex]->Values[VariableIndex]); + + // + // Sanitise strings. + // + if (!AsciiPropertyIsLegal (AsciiNVRAMKey)) { + DEBUG (( + DEBUG_WARN, + "NVRAM->LegacySchema[%u]->Key[%u] contains illegal character!\n", + GuidIndex, + VariableIndex + )); + ++ErrorCount; + } + } + } + + return ReportError (__func__, ErrorCount); +} diff --git a/Utilities/ocvalidate/ValidatePlatformInfo.c b/Utilities/ocvalidate/ValidatePlatformInfo.c new file mode 100644 index 00000000..8cf9ab00 --- /dev/null +++ b/Utilities/ocvalidate/ValidatePlatformInfo.c @@ -0,0 +1,87 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +#include + +// +// NOTE: Only PlatformInfo->Generic is checked here. The rest is ignored. +// +UINT32 +CheckPlatformInfo ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + EFI_STATUS Status; + OC_PLATFORM_CONFIG *UserPlatformInfo; + BOOLEAN IsAutomaticEnabled; + CONST CHAR8 *UpdateSMBIOSMode; + CONST CHAR8 *SystemProductName; + CONST CHAR8 *SystemMemoryStatus; + CONST CHAR8 *AsciiSystemUUID; + GUID Guid; + + DEBUG ((DEBUG_VERBOSE, "config loaded into PlatformInfo checker!\n")); + + ErrorCount = 0; + UserPlatformInfo = &Config->PlatformInfo; + IsAutomaticEnabled = UserPlatformInfo->Automatic; + UpdateSMBIOSMode = OC_BLOB_GET (&UserPlatformInfo->UpdateSmbiosMode); + SystemProductName = OC_BLOB_GET (&UserPlatformInfo->Generic.SystemProductName); + SystemMemoryStatus = OC_BLOB_GET (&UserPlatformInfo->Generic.SystemMemoryStatus); + AsciiSystemUUID = OC_BLOB_GET (&UserPlatformInfo->Generic.SystemUuid); + + if (AsciiStrCmp (UpdateSMBIOSMode, "TryOverwrite") != 0 + && AsciiStrCmp (UpdateSMBIOSMode, "Create") != 0 + && AsciiStrCmp (UpdateSMBIOSMode, "Overwrite") != 0 + && AsciiStrCmp (UpdateSMBIOSMode, "Custom") != 0) { + DEBUG ((DEBUG_WARN, "PlatformInfo->UpdateSMBIOSMode is borked (Can only be TryOverwrite, Create, Overwrite, or Custom)!\n")); + ++ErrorCount; + } + + if (!IsAutomaticEnabled) { + // + // This is not an error, but we need to stop checking further. + // + return ReportError (__func__, ErrorCount); + } + + if (!HasMacInfo (SystemProductName)) { + DEBUG ((DEBUG_WARN, "PlatformInfo->Generic->SystemProductName has unknown model set!\n")); + ++ErrorCount; + } + + if (AsciiStrCmp (SystemMemoryStatus, "Auto") != 0 + && AsciiStrCmp (SystemMemoryStatus, "Upgradable") != 0 + && AsciiStrCmp (SystemMemoryStatus, "Soldered") != 0) { + DEBUG ((DEBUG_WARN, "PlatformInfo->Generic->SystemMemoryStatus is borked (Can only be Auto, Upgradable, or Soldered)!\n")); + ++ErrorCount; + } + + Status = AsciiStrToGuid (AsciiSystemUUID, &Guid); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "PlatformInfo->Generic->SystemUUID is borked!\n")); + ++ErrorCount; + } + + // + // TODO: Sanitise MLB, ProcessorType, and SystemSerialNumber if possible... + // + + return ReportError (__func__, ErrorCount); +} diff --git a/Utilities/ocvalidate/ValidateUEFI.c b/Utilities/ocvalidate/ValidateUEFI.c new file mode 100644 index 00000000..aa36a93c --- /dev/null +++ b/Utilities/ocvalidate/ValidateUEFI.c @@ -0,0 +1,249 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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 "ocvalidate.h" +#include "OcValidateLib.h" + +#include + +UINT32 +CheckUEFI ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + UINT32 Index; + UINT32 Index2; + UINT32 IndexOpenUsbKbDxeEfiDriver; + UINT32 IndexPs2KeyboardDxeEfiDriver; + OC_UEFI_CONFIG *UserUefi; + OC_MISC_CONFIG *UserMisc; + CONST CHAR8 *Driver; + CONST CHAR8 *TextRenderer; + CONST CHAR8 *ConsoleMode; + CONST CHAR8 *PointerSupportMode; + CONST CHAR8 *KeySupportMode; + BOOLEAN HasOpenRuntimeEfiDriver; + BOOLEAN HasOpenUsbKbDxeEfiDriver; + BOOLEAN HasPs2KeyboardDxeEfiDriver; + BOOLEAN IsRequestBootVarRoutingEnabled; + BOOLEAN IsKeySupportEnabled; + BOOLEAN IsTextRendererSystem; + BOOLEAN IsClearScreenOnModeSwitchEnabled; + BOOLEAN IsIgnoreTextInGraphicsEnabled; + BOOLEAN IsReplaceTabWithSpaceEnabled; + BOOLEAN IsSanitiseClearScreenEnabled; + BOOLEAN IsPointerSupportEnabled; + CONST CHAR8 *Resolution; + UINT32 UserWidth; + UINT32 UserHeight; + UINT32 UserBpp; + BOOLEAN UserSetMax; + CONST CHAR8 *AsciiAudioDevicePath; + + DEBUG ((DEBUG_VERBOSE, "config loaded into UEFI checker!\n")); + + ErrorCount = 0; + IndexOpenUsbKbDxeEfiDriver = 0; + IndexPs2KeyboardDxeEfiDriver = 0; + UserUefi = &Config->Uefi; + UserMisc = &Config->Misc; + HasOpenRuntimeEfiDriver = FALSE; + HasOpenUsbKbDxeEfiDriver = FALSE; + HasPs2KeyboardDxeEfiDriver = FALSE; + IsRequestBootVarRoutingEnabled = UserUefi->Quirks.RequestBootVarRouting; + IsKeySupportEnabled = UserUefi->Input.KeySupport; + IsPointerSupportEnabled = UserUefi->Input.PointerSupport; + PointerSupportMode = OC_BLOB_GET (&UserUefi->Input.PointerSupportMode); + KeySupportMode = OC_BLOB_GET (&UserUefi->Input.KeySupportMode); + IsClearScreenOnModeSwitchEnabled = UserUefi->Output.ClearScreenOnModeSwitch; + IsIgnoreTextInGraphicsEnabled = UserUefi->Output.IgnoreTextInGraphics; + IsReplaceTabWithSpaceEnabled = UserUefi->Output.ReplaceTabWithSpace; + IsSanitiseClearScreenEnabled = UserUefi->Output.SanitiseClearScreen; + TextRenderer = OC_BLOB_GET (&UserUefi->Output.TextRenderer); + IsTextRendererSystem = FALSE; + ConsoleMode = OC_BLOB_GET (&UserUefi->Output.ConsoleMode); + Resolution = OC_BLOB_GET (&UserUefi->Output.Resolution); + AsciiAudioDevicePath = OC_BLOB_GET (&UserUefi->Audio.AudioDevice); + + // + // Sanitise strings. + // + if (AsciiStrCmp (TextRenderer, "BuiltinGraphics") != 0 + && AsciiStrCmp (TextRenderer, "BuiltinText") != 0 + && AsciiStrCmp (TextRenderer, "SystemGraphics") != 0 + && AsciiStrCmp (TextRenderer, "SystemText") != 0 + && AsciiStrCmp (TextRenderer, "SystemGeneric") != 0) { + DEBUG ((DEBUG_WARN, "UEFI->Output->TextRenderer is illegal (Can only be BuiltinGraphics, BuiltinText, SystemGraphics, SystemText, or SystemGeneric)!\n")); + ++ErrorCount; + } else if (AsciiStrnCmp (TextRenderer, "System", L_STR_LEN ("System")) == 0) { + // + // Check whether TextRenderer has System prefix. + // + IsTextRendererSystem = TRUE; + } + + // + // If FS restrictions is enabled but APFS FS scanning is disabled, it is an error. + // + if (UserUefi->Apfs.EnableJumpstart + && (UserMisc->Security.ScanPolicy & OC_SCAN_FILE_SYSTEM_LOCK) != 0 + && (UserMisc->Security.ScanPolicy & OC_SCAN_ALLOW_FS_APFS) == 0) { + DEBUG ((DEBUG_WARN, "UEFI->APFS->EnableJumpstart is enabled, but Misc->Security->ScanPolicy does not allow APFS scanning!\n")); + ++ErrorCount; + } + + if (AsciiAudioDevicePath[0] != '\0' && !AsciiDevicePathIsLegal (AsciiAudioDevicePath)) { + DEBUG ((DEBUG_WARN, "UEFI->Audio->AudioDevice is borked! Please check the information above!\n")); + ++ErrorCount; + } + + for (Index = 0; Index < UserUefi->Drivers.Count; ++Index) { + Driver = OC_BLOB_GET (UserUefi->Drivers.Values[Index]); + + // + // Sanitise strings. + // + if (!AsciiUefiDriverIsLegal (Driver)) { + DEBUG ((DEBUG_WARN, "UEFI->Drivers[%u] contains illegal character!\n", Index)); + ++ErrorCount; + continue; + } + + // + // Brute-force to check duplicated Drivers. + // + for (Index2 = Index + 1; Index2 < UserUefi->Drivers.Count; ++Index2) { + if (AsciiStrCmp (Driver, OC_BLOB_GET (UserUefi->Drivers.Values[Index2])) == 0) { + DEBUG (( + DEBUG_WARN, + "UEFI->Drivers[%u] and UEFI->Drivers[%u] (%a) are duplicated!\n", + Index, + Index2, + Driver + )); + ++ErrorCount; + } + } + + if (AsciiStrCmp (Driver, "OpenRuntime.efi") == 0) { + HasOpenRuntimeEfiDriver = TRUE; + } + if (AsciiStrCmp (Driver, "OpenUsbKbDxe.efi") == 0) { + HasOpenUsbKbDxeEfiDriver = TRUE; + IndexOpenUsbKbDxeEfiDriver = Index; + } + if (AsciiStrCmp (Driver, "Ps2KeyboardDxe.efi") == 0) { + HasPs2KeyboardDxeEfiDriver = TRUE; + IndexPs2KeyboardDxeEfiDriver = Index; + } + } + + if (IsPointerSupportEnabled && AsciiStrCmp (PointerSupportMode, "ASUS") != 0) { + DEBUG ((DEBUG_WARN, "UEFI->Input->PointerSupport is enabled, but PointerSupportMode is not ASUS!\n")); + ++ErrorCount; + } + + if (AsciiStrCmp (KeySupportMode, "Auto") != 0 + && AsciiStrCmp (KeySupportMode, "V1") != 0 + && AsciiStrCmp (KeySupportMode, "V2") != 0 + && AsciiStrCmp (KeySupportMode, "AMI") != 0) { + DEBUG ((DEBUG_WARN, "UEFI->Input->KeySupportMode is illegal (Can only be Auto, V1, V2, AMI)!\n")); + ++ErrorCount; + } + + if (IsRequestBootVarRoutingEnabled) { + if (!HasOpenRuntimeEfiDriver) { + DEBUG ((DEBUG_WARN, "UEFI->Quirks->RequestBootVarRouting is enabled, but OpenRuntime.efi is not loaded at UEFI->Drivers!\n")); + ++ErrorCount; + } + } + + if (IsKeySupportEnabled) { + if (HasOpenUsbKbDxeEfiDriver) { + DEBUG ((DEBUG_WARN, "OpenUsbKbDxe.efi at UEFI->Drivers[%u] should NEVER be used together with UEFI->Input->KeySupport!\n", IndexOpenUsbKbDxeEfiDriver)); + ++ErrorCount; + } + } else { + if (HasPs2KeyboardDxeEfiDriver) { + DEBUG ((DEBUG_WARN, "UEFI->Input->KeySupport should be enabled when Ps2KeyboardDxe.efi is in use!\n")); + ++ErrorCount; + } + } + + if (HasOpenUsbKbDxeEfiDriver && HasPs2KeyboardDxeEfiDriver) { + DEBUG (( + DEBUG_WARN, + "OpenUsbKbDxe.efi at UEFI->Drivers[%u], and Ps2KeyboardDxe.efi at UEFI->Drivers[%u], should NEVER co-exist!\n", + IndexOpenUsbKbDxeEfiDriver, + IndexPs2KeyboardDxeEfiDriver + )); + ++ErrorCount; + } + + if (!IsTextRendererSystem) { + if (IsClearScreenOnModeSwitchEnabled) { + DEBUG ((DEBUG_WARN, "UEFI->Output->ClearScreenOnModeSwitch is enabled on non-System TextRenderer (currently %a)!\n", TextRenderer)); + ++ErrorCount; + } + if (IsIgnoreTextInGraphicsEnabled) { + DEBUG ((DEBUG_WARN, "UEFI->Output->IgnoreTextInGraphics is enabled on non-System TextRenderer (currently %a)!\n", TextRenderer)); + ++ErrorCount; + } + if (IsReplaceTabWithSpaceEnabled) { + DEBUG ((DEBUG_WARN, "UEFI->Output->ReplaceTabWithSpace is enabled on non-System TextRenderer (currently %a)!\n", TextRenderer)); + ++ErrorCount; + } + if (IsSanitiseClearScreenEnabled) { + DEBUG ((DEBUG_WARN, "UEFI->Output->SanitiseClearScreen is enabled on non-System TextRenderer (currently %a)!\n", TextRenderer)); + ++ErrorCount; + } + } + + // + // Parse Output->ConsoleMode by calling OpenCore libraries. + // + OcParseConsoleMode ( + ConsoleMode, + &UserWidth, + &UserHeight, + &UserSetMax + ); + if (ConsoleMode[0] != '\0' + && !UserSetMax + && (UserWidth == 0 || UserHeight == 0)) { + DEBUG ((DEBUG_WARN, "UEFI->Output->ConsoleMode is borked, please check Configurations.pdf!\n")); + ++ErrorCount; + } + + // + // Parse Output->Resolution by calling OpenCore libraries. + // + OcParseScreenResolution ( + Resolution, + &UserWidth, + &UserHeight, + &UserBpp, + &UserSetMax + ); + if (Resolution[0] != '\0' + && !UserSetMax + && (UserWidth == 0 || UserHeight == 0)) { + DEBUG ((DEBUG_WARN, "UEFI->Output->Resolution is borked, please check Configurations.pdf!\n")); + ++ErrorCount; + } + + return ReportError (__func__, ErrorCount); +} diff --git a/Utilities/ocvalidate/config.plist b/Utilities/ocvalidate/config.plist deleted file mode 100644 index bd6aa1a3..00000000 --- a/Utilities/ocvalidate/config.plist +++ /dev/null @@ -1,71 +0,0 @@ - - - - - ACPI - - CleanHeaders - - - DeviceProperties - - DeviceName1 - - Prop1 - Val1 - Prop2 - Val2 - - DeviceName2 - - Prop21 - Val21 - Prop22 - Val22 - - - Misc - - DataFix - ESIz - DataVar - RFVm - DataMeta - 123 - DataMetaVar - MTIzAA== - DataMeta3 - - DataMeta4 - 65535 - Test32 - 123456 - - Mods - - Kext - - - Identifier - Hello - - - Identifier - Idt - - - - NVRAM - - COOL:boot-args - nopasaran=1 debug=1 - HACK:booter - #something - - SMBIOS - - ProductName - Computer2,1 - - - diff --git a/Utilities/ocvalidate/ocvalidate.c b/Utilities/ocvalidate/ocvalidate.c index ff33700f..c36b7c7f 100644 --- a/Utilities/ocvalidate/ocvalidate.c +++ b/Utilities/ocvalidate/ocvalidate.c @@ -1,5 +1,6 @@ /** @file Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. All rights reserved. All rights reserved. @@ -12,19 +13,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include -#include -#include -#include -#include - -#include -#include -#include -#include +#include "ocvalidate.h" +#include "OcValidateLib.h" #include -#include /* for fuzzing (TODO): @@ -34,41 +26,97 @@ rm -rf ConfigValidty.dSYM DICT fuzz*.log ConfigValidty */ +UINT32 +CheckConfig ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINT32 ErrorCount; + UINTN Index; + STATIC CONFIG_CHECK ConfigCheckers[] = { + &CheckACPI, + &CheckBooter, + &CheckDeviceProperties, + &CheckKernel, + &CheckMisc, + &CheckNVRAM, + &CheckPlatformInfo, + &CheckUEFI + }; -long long current_timestamp() { - struct timeval te; - gettimeofday(&te, NULL); // get current time - long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds - // printf("milliseconds: %lld\n", milliseconds); - return milliseconds; + ErrorCount = 0; + + // + // Pass config structure to all checkers. + // + for (Index = 0; Index < ARRAY_SIZE (ConfigCheckers); ++Index) { + ErrorCount += ConfigCheckers[Index] (Config); + } + + return ErrorCount; } -int main(int argc, char** argv) { - uint32_t f; - uint8_t *b; - if ((b = readFile(argc > 1 ? argv[1] : "config.plist", &f)) == NULL) { - printf("Read fail\n"); - return -1; - } - - long long a = current_timestamp(); - - PcdGet8 (PcdDebugPropertyMask) |= DEBUG_PROPERTY_DEBUG_CODE_ENABLED; - +int main(int argc, const char *argv[]) { + UINT8 *ConfigFileBuffer; + UINT32 ConfigFileSize; + CONST CHAR8 *ConfigFileName; + INT64 ExecTimeStart; OC_GLOBAL_CONFIG Config; EFI_STATUS Status; - Status = OcConfigurationInit (&Config, b, f); + UINT32 ErrorCount; - if (Status != EFI_SUCCESS) { - printf("Invalid config\n"); + // + // Enable PCD debug logging. + // + PcdGet8 (PcdDebugPropertyMask) |= DEBUG_PROPERTY_DEBUG_CODE_ENABLED; + PcdGet32 (PcdFixedDebugPrintErrorLevel) |= DEBUG_INFO; + PcdGet32 (PcdDebugPrintErrorLevel) |= DEBUG_INFO; + + // + // Read config file. + // + ConfigFileName = argc > 1 ? argv[1] : "config.plist"; + ConfigFileBuffer = readFile (ConfigFileName, &ConfigFileSize); + if (ConfigFileBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "Failed to read %a\n", ConfigFileName)); return -1; } - DEBUG ((DEBUG_ERROR, "Done checking %a in %llu ms\n", argc > 1 ? argv[1] : "./config.plist", current_timestamp() - a)); + // + // Record the current time when action starts. + // + ExecTimeStart = GetCurrentTimestamp (); + + // + // Initialise config structure to be checked, and exit on error. + // + Status = OcConfigurationInit (&Config, ConfigFileBuffer, ConfigFileSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Invalid config\n")); + return -1; + } + + ErrorCount = CheckConfig (&Config); + if (ErrorCount == 0) { + DEBUG (( + DEBUG_ERROR, + "Done checking %a in %llu ms\n", + ConfigFileName, + GetCurrentTimestamp () - ExecTimeStart + )); + } else { + DEBUG (( + DEBUG_ERROR, + "Done checking %a in %llu ms, but it has %u %a to be fixed\n", + ConfigFileName, + GetCurrentTimestamp () - ExecTimeStart, + ErrorCount, + ErrorCount > 1 ? "errors" : "error" + )); + } OcConfigurationFree (&Config); - - free(b); + free (ConfigFileBuffer); return 0; } diff --git a/Utilities/ocvalidate/ocvalidate.h b/Utilities/ocvalidate/ocvalidate.h new file mode 100644 index 00000000..79d4c7e2 --- /dev/null +++ b/Utilities/ocvalidate/ocvalidate.h @@ -0,0 +1,142 @@ +/** @file + Copyright (C) 2018, vit9696. All rights reserved. + Copyright (C) 2020, PMheart. 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. +**/ + +#ifndef OC_USER_UTILITIES_OCVALIDATE_H +#define OC_USER_UTILITIES_OCVALIDATE_H + +#include + +/** + Validate OpenCore Configuration. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +typedef +UINT32 +(*CONFIG_CHECK) ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration ACPI Section. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckACPI ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration Booter Section. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckBooter ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration DeviceProperties Section. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckDeviceProperties ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration Kernel Section. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckKernel ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration Misc Section. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckMisc ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration NVRAM Section. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckNVRAM ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration PlatformInfo Section. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckPlatformInfo ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration UEFI Section. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckUEFI ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Validate OpenCore Configuration overall, by calling each checker above. + + @param[in] Config Configuration structure. + + @return Number of errors detected. +**/ +UINT32 +CheckConfig ( + IN OC_GLOBAL_CONFIG *Config + ); + +#endif // OC_USER_UTILITIES_OCVALIDATE_H