From 3b5e4f10f6f38b6650f61ac94b60a9629c7bbba5 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Thu, 9 Feb 2023 08:35:21 -0700 Subject: [PATCH] kdk_handler.py: Use pkg receipts for KDK validation --- CHANGELOG.md | 1 + resources/kdk_handler.py | 54 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c6bc4cd..587bace9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Prioritizes KdkSupportPkg repository for downloads - Skips calls to Apple's now defunct Developer Portal API - Support local loose matching when no network connection is available + - Implement pkg receipt verification to validate integrity of KDKs - Implemented logging framework usage for more reliable logging - Logs are stored under `~/OpenCore-Patcher.log` - Subsequent runs are appended to the log, allowing for easy debugging diff --git a/resources/kdk_handler.py b/resources/kdk_handler.py index 92550f9f4..630677219 100644 --- a/resources/kdk_handler.py +++ b/resources/kdk_handler.py @@ -286,15 +286,63 @@ class KernelDebugKitObject: logging.error(f"- Failed to generate KDK Info.plist: {e}") - def _local_kdk_valid(self, kdk_path: str): + def _local_kdk_valid(self, kdk_path: Path): """ Validates provided KDK, ensure no corruption The reason for this is due to macOS deleting files from the KDK during OS updates, similar to how Install macOS.app is deleted during OS updates + Uses Apple's pkg receipt system to verify the original contents of the KDK + Args: - kdk_path (str): Path to KDK + kdk_path (Path): Path to KDK + + Returns: + bool: True if valid, False if invalid + """ + + if not Path(f"{kdk_path}/System/Library/CoreServices/SystemVersion.plist").exists(): + logging.info(f"- Corrupted KDK found ({kdk_path.name}), removing due to missing SystemVersion.plist") + self._remove_kdk(kdk_path) + return False + + # Get build from KDK + kdk_plist_data = plistlib.load(Path(f"{kdk_path}/System/Library/CoreServices/SystemVersion.plist").open("rb")) + if "ProductBuildVersion" not in kdk_plist_data: + logging.info(f"- Corrupted KDK found ({kdk_path.name}), removing due to missing ProductBuildVersion") + self._remove_kdk(kdk_path) + return False + + kdk_build = kdk_plist_data["ProductBuildVersion"] + + # Check pkg receipts for this build, will give a canonical list if all files that should be present + result = subprocess.run(["pkgutil", "--files", f"com.apple.pkg.KDK.{kdk_build}"], capture_output=True) + if result.returncode != 0: + # If pkg receipt is missing, we'll fallback to legacy validation + logging.info(f"- pkg receipt missing for {kdk_path.name}, falling back to legacy validation") + return self._local_kdk_valid_legacy(kdk_path) + + # Go through each line of the pkg receipt and ensure it exists + for line in result.stdout.decode("utf-8").splitlines(): + if not line.startswith("System/Library/Extensions"): + continue + if not Path(f"{kdk_path}/{line}").exists(): + logging.info(f"- Corrupted KDK found ({kdk_path.name}), removing due to missing file: {line}") + self._remove_kdk(kdk_path) + return False + + return True + + + def _local_kdk_valid_legacy(self, kdk_path: Path): + """ + Legacy variant of validating provided KDK + Uses best guess of files that should be present + This should ideally never be invoked, but used as a fallback + + Parameters: + kdk_path (Path): Path to KDK Returns: bool: True if valid, False if invalid @@ -307,8 +355,6 @@ class KernelDebugKitObject: "AMDRadeonX6000.kext/Contents/MacOS/AMDRadeonX6000", ] - kdk_path = Path(kdk_path) - for kext in KEXT_CATALOG: if not Path(f"{kdk_path}/System/Library/Extensions/{kext}").exists(): logging.info(f"- Corrupted KDK found, removing due to missing: {kdk_path}/System/Library/Extensions/{kext}")