diff options
Diffstat (limited to 'src/detection/bootmgr')
| -rw-r--r-- | src/detection/bootmgr/bootmgr.c | 44 | ||||
| -rw-r--r-- | src/detection/bootmgr/bootmgr.h | 13 | ||||
| -rw-r--r-- | src/detection/bootmgr/bootmgr_apple.c | 69 | ||||
| -rw-r--r-- | src/detection/bootmgr/bootmgr_bsd.c | 62 | ||||
| -rw-r--r-- | src/detection/bootmgr/bootmgr_haiku.cpp | 17 | ||||
| -rw-r--r-- | src/detection/bootmgr/bootmgr_linux.c | 32 | ||||
| -rw-r--r-- | src/detection/bootmgr/bootmgr_nosupport.c | 5 | ||||
| -rw-r--r-- | src/detection/bootmgr/bootmgr_windows.c | 74 | ||||
| -rw-r--r-- | src/detection/bootmgr/efi_helper.h | 22 |
9 files changed, 338 insertions, 0 deletions
diff --git a/src/detection/bootmgr/bootmgr.c b/src/detection/bootmgr/bootmgr.c new file mode 100644 index 0000000..31838d4 --- /dev/null +++ b/src/detection/bootmgr/bootmgr.c @@ -0,0 +1,44 @@ +#include "efi_helper.h" + +static inline uint8_t evBits(uint16_t val, uint8_t mask, uint8_t shift) { + return (uint8_t) ((val & (mask << shift)) >> shift); +} + +static void ffEfiUcs2ToUtf8(const uint16_t* const chars, FFstrbuf* result) { + for (uint32_t i = 0; chars[i]; i++) { + if (chars[i] <= 0x007f) { + ffStrbufAppendC(result, (char) chars[i]); + } else if (chars[i] > 0x007f && chars[i] <= 0x07ff) { + ffStrbufAppendC(result, (char) (0xc0 | evBits(chars[i], 0x1f, 6))); + ffStrbufAppendC(result, (char) (0x80 | evBits(chars[i], 0x3f, 0))); + } else { + ffStrbufAppendC(result, (char) (0xe0 | evBits(chars[i], 0xf, 12))); + ffStrbufAppendC(result, (char) (0x80 | evBits(chars[i], 0x3f, 6))); + ffStrbufAppendC(result, (char) (0x80 | evBits(chars[i], 0x3f, 0))); + } + } +} + +bool ffEfiFillLoadOption(const FFEfiLoadOption* efiOption, FFBootmgrResult* result) { + uint32_t descLen = 0; + while (efiOption->Description[descLen]) { + ++descLen; + } + + if (descLen) { + ffEfiUcs2ToUtf8(efiOption->Description, &result->name); + } + + for ( + ffEfiDevicePathProtocol* filePathList = (ffEfiDevicePathProtocol*) &efiOption->Description[descLen + 1]; + filePathList->Type != 0x7F; // End of Hardware Device Path + filePathList = (ffEfiDevicePathProtocol*) ((uint8_t*) filePathList + filePathList->Length)) { + if (filePathList->Type == 4 && filePathList->SubType == 4) { + // https://uefi.org/specs/UEFI/2.10/10_Protocols_Device_Path_Protocol.html#file-path-media-device-path + ffEfiUcs2ToUtf8((uint16_t*) filePathList->SpecificDevicePathData, &result->firmware); + return true; + } + } + + return false; +} diff --git a/src/detection/bootmgr/bootmgr.h b/src/detection/bootmgr/bootmgr.h new file mode 100644 index 0000000..055b0ee --- /dev/null +++ b/src/detection/bootmgr/bootmgr.h @@ -0,0 +1,13 @@ +#pragma once + +#include "fastfetch.h" +#include "modules/bootmgr/option.h" + +typedef struct FFBootmgrResult { + FFstrbuf name; + FFstrbuf firmware; + uint16_t order; + bool secureBoot; +} FFBootmgrResult; + +const char* ffDetectBootmgr(FFBootmgrResult* bios); diff --git a/src/detection/bootmgr/bootmgr_apple.c b/src/detection/bootmgr/bootmgr_apple.c new file mode 100644 index 0000000..d2b24e0 --- /dev/null +++ b/src/detection/bootmgr/bootmgr_apple.c @@ -0,0 +1,69 @@ +#include "bootmgr.h" +#include "common/io.h" +#include "common/apple/cf_helpers.h" + +#include <IOKit/IOKitLib.h> + +static const char* detectSecureBoot(bool* result) { +#if __aarch64__ + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen"); + if (!entryDevice) { + return "IORegistryEntryFromPath() failed"; + } + + FF_CFTYPE_AUTO_RELEASE CFTypeRef prop = IORegistryEntryCreateCFProperty(entryDevice, CFSTR("secure-boot"), kCFAllocatorDefault, kNilOptions); + if (!prop) { + return "IORegistryEntryCreateCFProperty() failed"; + } + + if (CFGetTypeID(prop) != CFDataGetTypeID() || CFDataGetLength((CFDataRef) prop) == 0) { + return "Invalid secure-boot property"; + } + + *result = (bool) *CFDataGetBytePtr((CFDataRef) prop); +#else + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/options"); + if (!entryDevice) { + return "IORegistryEntryFromPath() failed"; + } + + FF_CFTYPE_AUTO_RELEASE CFTypeRef prop = IORegistryEntryCreateCFProperty(entryDevice, CFSTR("94b73556-2197-4702-82a8-3e1337dafbfb:AppleSecureBootPolicy"), kCFAllocatorDefault, 0); + if (!prop) { + return "IORegistryEntryCreateCFProperty() failed"; + } + + if (CFGetTypeID(prop) != CFDataGetTypeID() || CFDataGetLength((CFDataRef) prop) == 0) { + return "Invalid secure-boot property"; + } + + *result = *CFDataGetBytePtr((CFDataRef) prop) != 0x02 /* Permissive Security */; +#endif + + return NULL; +} + +const char* ffDetectBootmgr(FFBootmgrResult* result) { + if (ffPathExists("/System/Library/CoreServices/boot.efi", FF_PATHTYPE_FILE)) { + ffStrbufSetStatic(&result->firmware, "/System/Library/CoreServices/boot.efi"); + } + + #ifdef __aarch64__ + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t deviceChosen = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen"); + if (deviceChosen) { + FF_CFTYPE_AUTO_RELEASE CFStringRef tag = IORegistryEntryCreateCFProperty(deviceChosen, CFSTR("iboot-stage-two-tag"), kCFAllocatorDefault, kNilOptions) + ?: IORegistryEntryCreateCFProperty(deviceChosen, CFSTR("system-firmware-version"), kCFAllocatorDefault, kNilOptions); + if (tag) { + ffCfStrGetString(tag, &result->name); + ffStrbufSubstrBeforeFirstC(&result->name, '-'); + } + } + #endif + + if (!result->name.length) { + ffStrbufSetStatic(&result->name, "iBoot"); + } + + detectSecureBoot(&result->secureBoot); + + return NULL; +} diff --git a/src/detection/bootmgr/bootmgr_bsd.c b/src/detection/bootmgr/bootmgr_bsd.c new file mode 100644 index 0000000..cfaa41d --- /dev/null +++ b/src/detection/bootmgr/bootmgr_bsd.c @@ -0,0 +1,62 @@ +#include "bootmgr.h" +#include "efi_helper.h" +#include "common/io.h" + +#ifdef __OpenBSD__ + #include <dev/efi/efiio.h> +#else + #include <sys/efiio.h> +#endif +#include <sys/ioctl.h> +#include <fcntl.h> +#include <stdalign.h> + +#ifdef __NetBSD__ +typedef uint16_t efi_char; +#endif + +#ifndef EFI_GLOBAL_VARIABLE + #define EFI_GLOBAL_VARIABLE { 0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, { 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c } } +#endif + +const char* ffDetectBootmgr(FFBootmgrResult* result) { + FF_AUTO_CLOSE_FD int efifd = open("/dev/efi", O_RDWR | O_CLOEXEC); + if (efifd < 0) { + return "open(/dev/efi) failed"; + } + + alignas(uint16_t) uint8_t buffer[2048]; + struct efi_var_ioc ioc = { + .vendor = EFI_GLOBAL_VARIABLE, + .data = buffer, + }; + + ioc.datasize = sizeof(buffer); + ioc.name = (efi_char[]) { 'B', 'o', 'o', 't', 'C', 'u', 'r', 'r', 'e', 'n', 't', '\0' }; + ioc.namesize = sizeof("BootCurrent") * 2; + if (ioctl(efifd, EFIIOC_VAR_GET, &ioc) < 0 || ioc.datasize != 2) { + return "ioctl(EFIIOC_VAR_GET, BootCurrent) failed"; + } + + result->order = *(uint16_t*) buffer; + + unsigned char hex[5]; + snprintf((char*) hex, sizeof(hex), "%04X", result->order); + ioc.datasize = sizeof(buffer); + ioc.name = (efi_char[]) { 'B', 'o', 'o', 't', hex[0], hex[1], hex[2], hex[3], '\0' }; + ioc.namesize = sizeof("Boot####") * 2; + if (ioctl(efifd, EFIIOC_VAR_GET, &ioc) < 0 || ioc.datasize == sizeof(buffer)) { + return "ioctl(EFIIOC_VAR_GET, Boot####) failed"; + } + + ffEfiFillLoadOption((FFEfiLoadOption*) buffer, result); + + ioc.name = (efi_char[]) { 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', '\0' }; + ioc.namesize = sizeof("SecureBoot") * 2; + ioc.datasize = sizeof(buffer); + if (ioctl(efifd, EFIIOC_VAR_GET, &ioc) == 0 && ioc.datasize == 1) { + result->secureBoot = !!buffer[0]; + } + + return NULL; +} diff --git a/src/detection/bootmgr/bootmgr_haiku.cpp b/src/detection/bootmgr/bootmgr_haiku.cpp new file mode 100644 index 0000000..72358fc --- /dev/null +++ b/src/detection/bootmgr/bootmgr_haiku.cpp @@ -0,0 +1,17 @@ +extern "C" { + #include "bootmgr.h" + #include "common/io.h" +} + +const char* ffDetectBootmgr(FFBootmgrResult* result) { + // TODO: glob haiku_loader.* + check EFI partition + if (ffPathExists("/system/haiku_loader.bios_ia32", FF_PATHTYPE_FILE)) { + ffStrbufSetStatic(&result->firmware, "/system/haiku_loader.bios_ia32"); + } + + ffStrbufSetStatic(&result->name, "haiku_loader"); + + // TODO: detectSecureBoot(&result->secureBoot); + + return NULL; +} diff --git a/src/detection/bootmgr/bootmgr_linux.c b/src/detection/bootmgr/bootmgr_linux.c new file mode 100644 index 0000000..0fd019a --- /dev/null +++ b/src/detection/bootmgr/bootmgr_linux.c @@ -0,0 +1,32 @@ +#include "bootmgr.h" +#include "common/io.h" +#include "efi_helper.h" + +#include <stdalign.h> + +#define FF_EFIVARS_PATH_PREFIX "/sys/firmware/efi/efivars/" + +const char* ffDetectBootmgr(FFBootmgrResult* result) { + alignas(uint16_t) uint8_t buffer[2048]; + + if (ffReadFileData(FF_EFIVARS_PATH_PREFIX "BootCurrent-" FF_EFI_GLOBAL_GUID, sizeof(buffer), buffer) != 6) { + return "Failed to read efivar: BootCurrent"; + } + + result->order = *(uint16_t*) &buffer[4]; + + snprintf((char*) buffer, sizeof(buffer), FF_EFIVARS_PATH_PREFIX "Boot%04X-" FF_EFI_GLOBAL_GUID, result->order); + + ssize_t size = ffReadFileData((const char*) buffer, sizeof(buffer), buffer); + if (size < 5 + (int) sizeof(FFEfiLoadOption) || size == (ssize_t) sizeof(buffer)) { + return "Failed to read efivar: Boot####"; + } + + ffEfiFillLoadOption((FFEfiLoadOption*) &buffer[4], result); + + if (ffReadFileData(FF_EFIVARS_PATH_PREFIX "SecureBoot-" FF_EFI_GLOBAL_GUID, sizeof(buffer), buffer) >= 5) { + result->secureBoot = buffer[4] == 1; + } + + return NULL; +} diff --git a/src/detection/bootmgr/bootmgr_nosupport.c b/src/detection/bootmgr/bootmgr_nosupport.c new file mode 100644 index 0000000..bb01714 --- /dev/null +++ b/src/detection/bootmgr/bootmgr_nosupport.c @@ -0,0 +1,5 @@ +#include "bootmgr.h" + +const char* ffDetectBootmgr(FF_A_UNUSED FFBootmgrResult* result) { + return "Not supported on this platform"; +} diff --git a/src/detection/bootmgr/bootmgr_windows.c b/src/detection/bootmgr/bootmgr_windows.c new file mode 100644 index 0000000..e83e1e2 --- /dev/null +++ b/src/detection/bootmgr/bootmgr_windows.c @@ -0,0 +1,74 @@ +#include "bootmgr.h" +#include "efi_helper.h" +#include "common/io.h" +#include "common/windows/nt.h" + +#include <ntstatus.h> +#include <windows.h> + +const char* enablePrivilege(const wchar_t* privilege) { + FF_AUTO_CLOSE_FD HANDLE token = NULL; + if (!NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))) { + return "NtOpenProcessToken() failed"; + } + + TOKEN_PRIVILEGES tp = { + .PrivilegeCount = 1, + .Privileges = { + (LUID_AND_ATTRIBUTES) { .Attributes = SE_PRIVILEGE_ENABLED } }, + }; + if (!LookupPrivilegeValueW(NULL, privilege, &tp.Privileges[0].Luid)) { + return "LookupPrivilegeValue() failed"; + } + + NTSTATUS status = NtAdjustPrivilegesToken(token, false, &tp, sizeof(tp), NULL, NULL); + if (!NT_SUCCESS(status)) { + return "NtAdjustPrivilegesToken() failed"; + } + + if (status == STATUS_NOT_ALL_ASSIGNED) { + return "The token does not have the specified privilege; try sudo please"; + } + + return NULL; +} + +const char* ffDetectBootmgr(FFBootmgrResult* result) { + const char* err = enablePrivilege(L"SeSystemEnvironmentPrivilege"); + if (err != NULL) { + return err; + } + + GUID efiGlobalGuid; + if (!NT_SUCCESS(RtlGUIDFromString(&(UNICODE_STRING) RTL_CONSTANT_STRING(L"{" FF_EFI_GLOBAL_GUID L"}"), &efiGlobalGuid))) { + return "RtlGUIDFromString() failed"; + } + + ULONG size = sizeof(result->order); + if (!NT_SUCCESS(NtQuerySystemEnvironmentValueEx(&(UNICODE_STRING) RTL_CONSTANT_STRING(L"BootCurrent"), &efiGlobalGuid, &result->order, &size, NULL))) { + return "NtQuerySystemEnvironmentValueEx(BootCurrent) failed"; + } + if (size != sizeof(result->order)) { + return "NtQuerySystemEnvironmentValueEx(BootCurrent) returned unexpected size"; + } + + uint8_t buffer[2048]; + wchar_t key[9]; + swprintf(key, ARRAY_SIZE(key), L"Boot%04X", result->order); + size = sizeof(buffer); + if (!NT_SUCCESS(NtQuerySystemEnvironmentValueEx(&(UNICODE_STRING) RTL_CONSTANT_STRING(key), &efiGlobalGuid, buffer, &size, NULL))) { + return "NtQuerySystemEnvironmentValueEx(Boot####) failed"; + } + if (size < sizeof(FFEfiLoadOption) || size == ARRAY_SIZE(buffer)) { + return "NtQuerySystemEnvironmentValueEx(Boot####) returned unexpected size"; + } + + ffEfiFillLoadOption((FFEfiLoadOption*) buffer, result); + + SYSTEM_SECUREBOOT_INFORMATION ssi; + if (NT_SUCCESS(NtQuerySystemInformation(SystemSecureBootInformation, &ssi, sizeof(ssi), NULL))) { + result->secureBoot = ssi.SecureBootEnabled; + } + + return NULL; +} diff --git a/src/detection/bootmgr/efi_helper.h b/src/detection/bootmgr/efi_helper.h new file mode 100644 index 0000000..b52ec8a --- /dev/null +++ b/src/detection/bootmgr/efi_helper.h @@ -0,0 +1,22 @@ +#include "bootmgr.h" + +// https://uefi.org/specs/UEFI/2.10/10_Protocols_Device_Path_Protocol.html#generic-device-path-structures +typedef struct ffEfiDevicePathProtocol { + uint8_t Type; + uint8_t SubType; + uint16_t Length; + uint8_t SpecificDevicePathData[]; +} ffEfiDevicePathProtocol; + +// https://uefi.org/specs/UEFI/2.10/03_Boot_Manager.html#load-options +typedef struct FFEfiLoadOption { + uint32_t Attributes; + uint16_t FilePathListLength; + uint16_t Description[]; + // ffEfiDevicePathProtocol FilePathList[]; + // uint8_t OptionalData[]; +} FFEfiLoadOption; + +bool ffEfiFillLoadOption(const FFEfiLoadOption* efiOption, FFBootmgrResult* result); + +#define FF_EFI_GLOBAL_GUID "8be4df61-93ca-11d2-aa0d-00e098032b8c" |