diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:49:52 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:49:52 +0000 |
commit | 55944e5e40b1be2afc4855d8d2baf4b73d1876b5 (patch) | |
tree | 33f869f55a1b149e9b7c2b7e201867ca5dd52992 /src/fundamental | |
parent | Initial commit. (diff) | |
download | systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.tar.xz systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.zip |
Adding upstream version 255.4.upstream/255.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fundamental')
-rw-r--r-- | src/fundamental/bootspec-fundamental.c | 62 | ||||
-rw-r--r-- | src/fundamental/bootspec-fundamental.h | 17 | ||||
-rw-r--r-- | src/fundamental/confidential-virt-fundamental.h | 72 | ||||
-rw-r--r-- | src/fundamental/efivars-fundamental.c | 37 | ||||
-rw-r--r-- | src/fundamental/efivars-fundamental.h | 50 | ||||
-rw-r--r-- | src/fundamental/logarithm.h | 59 | ||||
-rw-r--r-- | src/fundamental/macro-fundamental.h | 515 | ||||
-rw-r--r-- | src/fundamental/memory-util-fundamental.h | 108 | ||||
-rw-r--r-- | src/fundamental/meson.build | 11 | ||||
-rw-r--r-- | src/fundamental/sbat.h | 14 | ||||
-rw-r--r-- | src/fundamental/sha256.c | 285 | ||||
-rw-r--r-- | src/fundamental/sha256.h | 39 | ||||
-rw-r--r-- | src/fundamental/string-util-fundamental.c | 228 | ||||
-rw-r--r-- | src/fundamental/string-util-fundamental.h | 121 | ||||
-rw-r--r-- | src/fundamental/tpm2-pcr.h | 51 | ||||
-rw-r--r-- | src/fundamental/uki.c | 23 | ||||
-rw-r--r-- | src/fundamental/uki.h | 28 | ||||
-rw-r--r-- | src/fundamental/unaligned-fundamental.h | 40 |
18 files changed, 1760 insertions, 0 deletions
diff --git a/src/fundamental/bootspec-fundamental.c b/src/fundamental/bootspec-fundamental.c new file mode 100644 index 0000000..b2841e3 --- /dev/null +++ b/src/fundamental/bootspec-fundamental.c @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "bootspec-fundamental.h" + +bool bootspec_pick_name_version_sort_key( + const sd_char *os_pretty_name, + const sd_char *os_image_id, + const sd_char *os_name, + const sd_char *os_id, + const sd_char *os_image_version, + const sd_char *os_version, + const sd_char *os_version_id, + const sd_char *os_build_id, + const sd_char **ret_name, + const sd_char **ret_version, + const sd_char **ret_sort_key) { + + const sd_char *good_name, *good_version, *good_sort_key; + + /* Find the best human readable title, version string and sort key for a boot entry (using the + * os-release(5) fields). Precise is preferred over vague, and human readable over machine + * readable. Thus: + * + * 1. First priority gets the PRETTY_NAME field, which is the primary string intended for display, + * and should already contain both a nice description and a version indication (if that concept + * applies). + * + * 2. Otherwise we go for IMAGE_ID and IMAGE_VERSION (thus we show details about the image, + * i.e. specific combination of packages and configuration), if that concept applies. + * + * 3. Otherwise we go for NAME and VERSION (i.e. human readable OS name and version) + * + * 4. Otherwise we go for ID and VERSION_ID (i.e. machine readable OS name and version) + * + * 5. Finally, for the version we'll use BUILD_ID (i.e. a machine readable version that identifies + * the original OS build used during installation) + * + * Note that the display logic will show only the name by default, except if that isn't unique in + * which case the version is shown too. + * + * Note that name/version determined here are used only for display purposes. Boot entry preference + * sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the sort key (if + * defined) or entry "id" string (i.e. entry file name) otherwise. */ + + good_name = os_pretty_name ?: (os_image_id ?: (os_name ?: os_id)); + good_version = os_image_version ?: (os_version ?: (os_version_id ?: os_build_id)); + good_sort_key = os_image_id ?: os_id; + + if (!good_name) + return false; + + if (ret_name) + *ret_name = good_name; + + if (ret_version) + *ret_version = good_version; + + if (ret_sort_key) + *ret_sort_key = good_sort_key; + + return true; +} diff --git a/src/fundamental/bootspec-fundamental.h b/src/fundamental/bootspec-fundamental.h new file mode 100644 index 0000000..19b489c --- /dev/null +++ b/src/fundamental/bootspec-fundamental.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "string-util-fundamental.h" + +bool bootspec_pick_name_version_sort_key( + const sd_char *os_pretty_name, + const sd_char *os_image_id, + const sd_char *os_name, + const sd_char *os_id, + const sd_char *os_image_version, + const sd_char *os_version, + const sd_char *os_version_id, + const sd_char *os_build_id, + const sd_char **ret_name, + const sd_char **ret_version, + const sd_char **ret_sort_key); diff --git a/src/fundamental/confidential-virt-fundamental.h b/src/fundamental/confidential-virt-fundamental.h new file mode 100644 index 0000000..986923e --- /dev/null +++ b/src/fundamental/confidential-virt-fundamental.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <stdint.h> + +/* Keep CVM detection logic in this file at feature parity with + * that in src/efi/boot/vmm.c */ + +#define CPUID_PROCESSOR_INFO_AND_FEATURE_BITS UINT32_C(0x1) + +/* + * AMD64 Architecture Programmer’s Manual Volume 3: + * General-Purpose and System Instructions. + * Chapter: E4.1 - Maximum Extended Function Number and Vendor String + * https://www.amd.com/system/files/TechDocs/24594.pdf + */ +#define CPUID_GET_HIGHEST_FUNCTION UINT32_C(0x80000000) + +/* + * AMD64 Architecture Programmer’s Manual Volume 3: + * General-Purpose and System Instructions. + * Chapter: E4.17 - Encrypted Memory Capabilities + * https://www.amd.com/system/files/TechDocs/24594.pdf + */ +#define CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES UINT32_C(0x8000001f) + +/* + * AMD64 Architecture Programmer’s Manual Volume 3: + * General-Purpose and System Instructions. + * Chapter: 15.34.10 - SEV_STATUS MSR + * https://www.amd.com/system/files/TechDocs/24593.pdf + */ +#define MSR_AMD64_SEV UINT32_C(0xc0010131) + +/* + * Intel® TDX Module v1.5 Base Architecture Specification + * Chapter: 11.2 + * https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html + */ + +#define CPUID_INTEL_TDX_ENUMERATION UINT32_C(0x21) + +/* Requirements for Implementing the Microsoft Hypervisor Interface + * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs + */ +#define CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS UINT32_C(0x40000000) + +#define CPUID_HYPERV_FEATURES UINT32_C(0x40000003) + +#define CPUID_HYPERV_ISOLATION_CONFIG UINT32_C(0x4000000C) + +#define CPUID_HYPERV_MIN UINT32_C(0x40000005) +#define CPUID_HYPERV_MAX UINT32_C(0x4000ffff) + +#define CPUID_SIG_AMD "AuthenticAMD" +#define CPUID_SIG_INTEL "GenuineIntel" +#define CPUID_SIG_INTEL_TDX "IntelTDX " +#define CPUID_SIG_HYPERV "Microsoft Hv" + +/* ecx bit 31: set => hyperpvisor, unset => bare metal */ +#define CPUID_FEATURE_HYPERVISOR (UINT32_C(1) << 31) + +/* Linux include/asm-generic/hyperv-tlfs.h */ +#define CPUID_HYPERV_CPU_MANAGEMENT (UINT32_C(1) << 12) /* root partition */ +#define CPUID_HYPERV_ISOLATION (UINT32_C(1) << 22) /* confidential VM partition */ + +#define CPUID_HYPERV_ISOLATION_TYPE_MASK UINT32_C(0xf) +#define CPUID_HYPERV_ISOLATION_TYPE_SNP 2 + +#define EAX_SEV (UINT32_C(1) << 1) +#define MSR_SEV (UINT64_C(1) << 0) +#define MSR_SEV_ES (UINT64_C(1) << 1) +#define MSR_SEV_SNP (UINT64_C(1) << 2) diff --git a/src/fundamental/efivars-fundamental.c b/src/fundamental/efivars-fundamental.c new file mode 100644 index 0000000..2ec3bfb --- /dev/null +++ b/src/fundamental/efivars-fundamental.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "efivars-fundamental.h" + +static const sd_char * const table[_SECURE_BOOT_MAX] = { + [SECURE_BOOT_UNSUPPORTED] = STR_C("unsupported"), + [SECURE_BOOT_DISABLED] = STR_C("disabled"), + [SECURE_BOOT_UNKNOWN] = STR_C("unknown"), + [SECURE_BOOT_AUDIT] = STR_C("audit"), + [SECURE_BOOT_DEPLOYED] = STR_C("deployed"), + [SECURE_BOOT_SETUP] = STR_C("setup"), + [SECURE_BOOT_USER] = STR_C("user"), +}; + +const sd_char *secure_boot_mode_to_string(SecureBootMode m) { + return (m >= 0 && m < _SECURE_BOOT_MAX) ? table[m] : NULL; +} + +SecureBootMode decode_secure_boot_mode(bool secure, bool audit, bool deployed, bool setup) { + /* See figure 32-4 Secure Boot Modes from UEFI Specification 2.9 */ + if (secure && deployed && !audit && !setup) + return SECURE_BOOT_DEPLOYED; + if (secure && !deployed && !audit && !setup) + return SECURE_BOOT_USER; + if (!secure && !deployed && audit && setup) + return SECURE_BOOT_AUDIT; + if (!secure && !deployed && !audit && setup) + return SECURE_BOOT_SETUP; + + /* Some firmware allows disabling secure boot while not being in + * setup mode unless the PK is cleared. */ + if (!secure && !deployed && !audit && !setup) + return SECURE_BOOT_DISABLED; + + /* Well, this should not happen. */ + return SECURE_BOOT_UNKNOWN; +} diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h new file mode 100644 index 0000000..2d25d22 --- /dev/null +++ b/src/fundamental/efivars-fundamental.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#ifdef SD_BOOT +# define EINVAL 22 +#else +# include <errno.h> +#endif +#include "string-util-fundamental.h" + +/* Features of the loader, i.e. systemd-boot */ +#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT (UINT64_C(1) << 0) +#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (UINT64_C(1) << 1) +#define EFI_LOADER_FEATURE_ENTRY_DEFAULT (UINT64_C(1) << 2) +#define EFI_LOADER_FEATURE_ENTRY_ONESHOT (UINT64_C(1) << 3) +#define EFI_LOADER_FEATURE_BOOT_COUNTING (UINT64_C(1) << 4) +#define EFI_LOADER_FEATURE_XBOOTLDR (UINT64_C(1) << 5) +#define EFI_LOADER_FEATURE_RANDOM_SEED (UINT64_C(1) << 6) +#define EFI_LOADER_FEATURE_LOAD_DRIVER (UINT64_C(1) << 7) +#define EFI_LOADER_FEATURE_SORT_KEY (UINT64_C(1) << 8) +#define EFI_LOADER_FEATURE_SAVED_ENTRY (UINT64_C(1) << 9) +#define EFI_LOADER_FEATURE_DEVICETREE (UINT64_C(1) << 10) +#define EFI_LOADER_FEATURE_SECUREBOOT_ENROLL (UINT64_C(1) << 11) +#define EFI_LOADER_FEATURE_RETAIN_SHIM (UINT64_C(1) << 12) +#define EFI_LOADER_FEATURE_MENU_DISABLE (UINT64_C(1) << 13) + +/* Features of the stub, i.e. systemd-stub */ +#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0) +#define EFI_STUB_FEATURE_PICK_UP_CREDENTIALS (UINT64_C(1) << 1) +#define EFI_STUB_FEATURE_PICK_UP_SYSEXTS (UINT64_C(1) << 2) +#define EFI_STUB_FEATURE_THREE_PCRS (UINT64_C(1) << 3) +#define EFI_STUB_FEATURE_RANDOM_SEED (UINT64_C(1) << 4) +#define EFI_STUB_FEATURE_CMDLINE_ADDONS (UINT64_C(1) << 5) +#define EFI_STUB_FEATURE_CMDLINE_SMBIOS (UINT64_C(1) << 6) +#define EFI_STUB_FEATURE_DEVICETREE_ADDONS (UINT64_C(1) << 7) + +typedef enum SecureBootMode { + SECURE_BOOT_UNSUPPORTED, + SECURE_BOOT_DISABLED, + SECURE_BOOT_UNKNOWN, + SECURE_BOOT_AUDIT, + SECURE_BOOT_DEPLOYED, + SECURE_BOOT_SETUP, + SECURE_BOOT_USER, + _SECURE_BOOT_MAX, + _SECURE_BOOT_INVALID = -EINVAL, +} SecureBootMode; + +const sd_char *secure_boot_mode_to_string(SecureBootMode m); +SecureBootMode decode_secure_boot_mode(bool secure, bool audit, bool deployed, bool setup); diff --git a/src/fundamental/logarithm.h b/src/fundamental/logarithm.h new file mode 100644 index 0000000..0b03bbd --- /dev/null +++ b/src/fundamental/logarithm.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdint.h> + +/* Note: log2(0) == log2(1) == 0 here and below. */ + +#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0) +#define NONCONST_LOG2ULL(x) ({ \ + unsigned long long _x = (x); \ + _x > 1 ? (unsigned) __builtin_clzll(_x) ^ 63U : 0; \ + }) +#define LOG2ULL(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2ULL(x), NONCONST_LOG2ULL(x)) + +static inline unsigned log2u64(uint64_t x) { +#if __SIZEOF_LONG_LONG__ == 8 + return LOG2ULL(x); +#else +# error "Wut?" +#endif +} + +static inline unsigned u32ctz(uint32_t n) { +#if __SIZEOF_INT__ == 4 + return n != 0 ? __builtin_ctz(n) : 32; +#else +# error "Wut?" +#endif +} + +#define popcount(n) \ + _Generic((n), \ + unsigned char: __builtin_popcount(n), \ + unsigned short: __builtin_popcount(n), \ + unsigned: __builtin_popcount(n), \ + unsigned long: __builtin_popcountl(n), \ + unsigned long long: __builtin_popcountll(n)) + +#define CONST_LOG2U(x) ((x) > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1 : 0) +#define NONCONST_LOG2U(x) ({ \ + unsigned _x = (x); \ + _x > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(_x) - 1 : 0; \ + }) +#define LOG2U(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2U(x), NONCONST_LOG2U(x)) + +static inline unsigned log2i(int x) { + return LOG2U(x); +} + +static inline unsigned log2u(unsigned x) { + return LOG2U(x); +} + +static inline unsigned log2u_round_up(unsigned x) { + if (x <= 1) + return 0; + + return log2u(x - 1) + 1; +} diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h new file mode 100644 index 0000000..797330d --- /dev/null +++ b/src/fundamental/macro-fundamental.h @@ -0,0 +1,515 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#if !SD_BOOT +# include <assert.h> +#endif + +#include <limits.h> +#include <stdalign.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +/* Temporarily disable some warnings */ +#define DISABLE_WARNING_DEPRECATED_DECLARATIONS \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + +#define DISABLE_WARNING_FORMAT_NONLITERAL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") + +#define DISABLE_WARNING_MISSING_PROTOTYPES \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") + +#define DISABLE_WARNING_NONNULL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wnonnull\"") + +#define DISABLE_WARNING_SHADOW \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wshadow\"") + +#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") + +#define DISABLE_WARNING_TYPE_LIMITS \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") + +#define DISABLE_WARNING_ADDRESS \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Waddress\"") + +#define REENABLE_WARNING \ + _Pragma("GCC diagnostic pop") + +#define _align_(x) __attribute__((__aligned__(x))) +#define _alignas_(x) __attribute__((__aligned__(alignof(x)))) +#define _alignptr_ __attribute__((__aligned__(sizeof(void *)))) +#define _cleanup_(x) __attribute__((__cleanup__(x))) +#define _const_ __attribute__((__const__)) +#define _deprecated_ __attribute__((__deprecated__)) +#define _destructor_ __attribute__((__destructor__)) +#define _hidden_ __attribute__((__visibility__("hidden"))) +#define _likely_(x) (__builtin_expect(!!(x), 1)) +#define _malloc_ __attribute__((__malloc__)) +#define _noinline_ __attribute__((noinline)) +#define _noreturn_ _Noreturn +#define _packed_ __attribute__((__packed__)) +#define _printf_(a, b) __attribute__((__format__(printf, a, b))) +#define _public_ __attribute__((__visibility__("default"))) +#define _pure_ __attribute__((__pure__)) +#define _retain_ __attribute__((__retain__)) +#define _returns_nonnull_ __attribute__((__returns_nonnull__)) +#define _section_(x) __attribute__((__section__(x))) +#define _sentinel_ __attribute__((__sentinel__)) +#define _unlikely_(x) (__builtin_expect(!!(x), 0)) +#define _unused_ __attribute__((__unused__)) +#define _used_ __attribute__((__used__)) +#define _warn_unused_result_ __attribute__((__warn_unused_result__)) +#define _weak_ __attribute__((__weak__)) +#define _weakref_(x) __attribute__((__weakref__(#x))) + +#ifdef __clang__ +# define _alloc_(...) +#else +# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) +#endif + +#if __GNUC__ >= 7 || (defined(__clang__) && __clang_major__ >= 10) +# define _fallthrough_ __attribute__((__fallthrough__)) +#else +# define _fallthrough_ +#endif + +#define XSTRINGIFY(x) #x +#define STRINGIFY(x) XSTRINGIFY(x) + +#ifndef __COVERITY__ +# define VOID_0 ((void)0) +#else +# define VOID_0 ((void*)0) +#endif + +#define ELEMENTSOF(x) \ + (__builtin_choose_expr( \ + !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ + sizeof(x)/sizeof((x)[0]), \ + VOID_0)) + +#define XCONCATENATE(x, y) x ## y +#define CONCATENATE(x, y) XCONCATENATE(x, y) + +#if SD_BOOT + _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function); + + #ifdef NDEBUG + #define assert(expr) ({ if (!(expr)) __builtin_unreachable(); }) + #define assert_not_reached() __builtin_unreachable() + #else + #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); }) + #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__) + #endif + #define static_assert _Static_assert + #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); }) +#endif + +/* This passes the argument through after (if asserts are enabled) checking that it is not null. */ +#define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert) +#define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se) +#define _ASSERT_PTR(expr, var, check) \ + ({ \ + typeof(expr) var = (expr); \ + check(var); \ + var; \ + }) + +#define ASSERT_NONNEG(expr) \ + ({ \ + typeof(expr) _expr_ = (expr), _zero = 0; \ + assert(_expr_ >= _zero); \ + _expr_; \ + }) + +#define ASSERT_SE_NONNEG(expr) \ + ({ \ + typeof(expr) _expr_ = (expr), _zero = 0; \ + assert_se(_expr_ >= _zero); \ + _expr_; \ + }) + +#define assert_cc(expr) static_assert(expr, #expr) + +#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) +#define UNIQ __COUNTER__ + +/* Note that this works differently from pthread_once(): this macro does + * not synchronize code execution, i.e. code that is run conditionalized + * on this macro will run concurrently to all other code conditionalized + * the same way, there's no ordering or completion enforced. */ +#define ONCE __ONCE(UNIQ_T(_once_, UNIQ)) +#define __ONCE(o) \ + ({ \ + static bool (o) = false; \ + __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \ + }) + +#undef MAX +#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) +#define __MAX(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ + }) + +#define IS_UNSIGNED_INTEGER_TYPE(type) \ + (__builtin_types_compatible_p(typeof(type), unsigned char) || \ + __builtin_types_compatible_p(typeof(type), unsigned short) || \ + __builtin_types_compatible_p(typeof(type), unsigned) || \ + __builtin_types_compatible_p(typeof(type), unsigned long) || \ + __builtin_types_compatible_p(typeof(type), unsigned long long)) + +#define IS_SIGNED_INTEGER_TYPE(type) \ + (__builtin_types_compatible_p(typeof(type), signed char) || \ + __builtin_types_compatible_p(typeof(type), signed short) || \ + __builtin_types_compatible_p(typeof(type), signed) || \ + __builtin_types_compatible_p(typeof(type), signed long) || \ + __builtin_types_compatible_p(typeof(type), signed long long)) + +/* Evaluates to (void) if _A or _B are not constant or of different types (being integers of different sizes + * is also OK as long as the signedness matches) */ +#define CONST_MAX(_A, _B) \ + (__builtin_choose_expr( \ + __builtin_constant_p(_A) && \ + __builtin_constant_p(_B) && \ + (__builtin_types_compatible_p(typeof(_A), typeof(_B)) || \ + (IS_UNSIGNED_INTEGER_TYPE(_A) && IS_UNSIGNED_INTEGER_TYPE(_B)) || \ + (IS_SIGNED_INTEGER_TYPE(_A) && IS_SIGNED_INTEGER_TYPE(_B))), \ + ((_A) > (_B)) ? (_A) : (_B), \ + VOID_0)) + +/* takes two types and returns the size of the larger one */ +#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) + +#define MAX3(x, y, z) \ + ({ \ + const typeof(x) _c = MAX(x, y); \ + MAX(_c, z); \ + }) + +#define MAX4(x, y, z, a) \ + ({ \ + const typeof(x) _d = MAX3(x, y, z); \ + MAX(_d, a); \ + }) + +#undef MIN +#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) +#define __MIN(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ + }) + +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define CONST_MIN(_A, _B) \ + (__builtin_choose_expr( \ + __builtin_constant_p(_A) && \ + __builtin_constant_p(_B) && \ + __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ + ((_A) < (_B)) ? (_A) : (_B), \ + VOID_0)) + +#define MIN3(x, y, z) \ + ({ \ + const typeof(x) _c = MIN(x, y); \ + MIN(_c, z); \ + }) + +/* Returns true if the passed integer is a positive power of two */ +#define CONST_ISPOWEROF2(x) \ + ((x) > 0 && ((x) & ((x) - 1)) == 0) + +#define ISPOWEROF2(x) \ + __builtin_choose_expr( \ + __builtin_constant_p(x), \ + CONST_ISPOWEROF2(x), \ + ({ \ + const typeof(x) _x = (x); \ + CONST_ISPOWEROF2(_x); \ + })) + +#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) +#define __LESS_BY(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \ + }) + +#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b)) +#define __CMP(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \ + UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \ + }) + +#undef CLAMP +#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) +#define __CLAMP(xq, x, lowq, low, highq, high) \ + ({ \ + const typeof(x) UNIQ_T(X, xq) = (x); \ + const typeof(low) UNIQ_T(LOW, lowq) = (low); \ + const typeof(high) UNIQ_T(HIGH, highq) = (high); \ + UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \ + UNIQ_T(HIGH, highq) : \ + UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \ + UNIQ_T(LOW, lowq) : \ + UNIQ_T(X, xq); \ + }) + +/* [(x + y - 1) / y] suffers from an integer overflow, even though the + * computation should be possible in the given type. Therefore, we use + * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the + * quotient and the remainder, so both should be equally fast. */ +#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y)) +#define __DIV_ROUND_UP(xq, x, yq, y) \ + ({ \ + const typeof(x) UNIQ_T(X, xq) = (x); \ + const typeof(y) UNIQ_T(Y, yq) = (y); \ + (UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \ + }) + +/* Rounds up x to the next multiple of y. Resolves to typeof(x) -1 in case of overflow */ +#define __ROUND_UP(q, x, y) \ + ({ \ + const typeof(y) UNIQ_T(A, q) = (y); \ + const typeof(x) UNIQ_T(B, q) = DIV_ROUND_UP((x), UNIQ_T(A, q)); \ + typeof(x) UNIQ_T(C, q); \ + __builtin_mul_overflow(UNIQ_T(B, q), UNIQ_T(A, q), &UNIQ_T(C, q)) ? (typeof(x)) -1 : UNIQ_T(C, q); \ + }) +#define ROUND_UP(x, y) __ROUND_UP(UNIQ, (x), (y)) + +#define CASE_F_1(X) case X: +#define CASE_F_2(X, ...) case X: CASE_F_1( __VA_ARGS__) +#define CASE_F_3(X, ...) case X: CASE_F_2( __VA_ARGS__) +#define CASE_F_4(X, ...) case X: CASE_F_3( __VA_ARGS__) +#define CASE_F_5(X, ...) case X: CASE_F_4( __VA_ARGS__) +#define CASE_F_6(X, ...) case X: CASE_F_5( __VA_ARGS__) +#define CASE_F_7(X, ...) case X: CASE_F_6( __VA_ARGS__) +#define CASE_F_8(X, ...) case X: CASE_F_7( __VA_ARGS__) +#define CASE_F_9(X, ...) case X: CASE_F_8( __VA_ARGS__) +#define CASE_F_10(X, ...) case X: CASE_F_9( __VA_ARGS__) +#define CASE_F_11(X, ...) case X: CASE_F_10( __VA_ARGS__) +#define CASE_F_12(X, ...) case X: CASE_F_11( __VA_ARGS__) +#define CASE_F_13(X, ...) case X: CASE_F_12( __VA_ARGS__) +#define CASE_F_14(X, ...) case X: CASE_F_13( __VA_ARGS__) +#define CASE_F_15(X, ...) case X: CASE_F_14( __VA_ARGS__) +#define CASE_F_16(X, ...) case X: CASE_F_15( __VA_ARGS__) +#define CASE_F_17(X, ...) case X: CASE_F_16( __VA_ARGS__) +#define CASE_F_18(X, ...) case X: CASE_F_17( __VA_ARGS__) +#define CASE_F_19(X, ...) case X: CASE_F_18( __VA_ARGS__) +#define CASE_F_20(X, ...) case X: CASE_F_19( __VA_ARGS__) + +#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME +#define FOR_EACH_MAKE_CASE(...) \ + GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \ + CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ + (__VA_ARGS__) + +#define IN_SET(x, first, ...) \ + ({ \ + bool _found = false; \ + /* If the build breaks in the line below, you need to extend the case macros. We use typeof(+x) \ + * here to widen the type of x if it is a bit-field as this would otherwise be illegal. */ \ + static const typeof(+x) __assert_in_set[] _unused_ = { first, __VA_ARGS__ }; \ + assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ + switch (x) { \ + FOR_EACH_MAKE_CASE(first, __VA_ARGS__) \ + _found = true; \ + break; \ + default: \ + break; \ + } \ + _found; \ + }) + +/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time + * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ +#define TAKE_GENERIC(var, type, nullvalue) \ + ({ \ + type *_pvar_ = &(var); \ + type _var_ = *_pvar_; \ + type _nullvalue_ = nullvalue; \ + *_pvar_ = _nullvalue_; \ + _var_; \ + }) +#define TAKE_PTR_TYPE(ptr, type) TAKE_GENERIC(ptr, type, NULL) +#define TAKE_PTR(ptr) TAKE_PTR_TYPE(ptr, typeof(ptr)) +#define TAKE_STRUCT_TYPE(s, type) TAKE_GENERIC(s, type, {}) +#define TAKE_STRUCT(s) TAKE_STRUCT_TYPE(s, typeof(s)) + +/* + * STRLEN - return the length of a string literal, minus the trailing NUL byte. + * Contrary to strlen(), this is a constant expression. + * @x: a string literal. + */ +#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0]))) + +#define mfree(memory) \ + ({ \ + free(memory); \ + (typeof(memory)) NULL; \ + }) + +static inline size_t ALIGN_TO(size_t l, size_t ali) { + assert(ISPOWEROF2(ali)); + + if (l > SIZE_MAX - (ali - 1)) + return SIZE_MAX; /* indicate overflow */ + + return ((l + (ali - 1)) & ~(ali - 1)); +} + +static inline uint64_t ALIGN_TO_U64(uint64_t l, uint64_t ali) { + assert(ISPOWEROF2(ali)); + + if (l > UINT64_MAX - (ali - 1)) + return UINT64_MAX; /* indicate overflow */ + + return ((l + (ali - 1)) & ~(ali - 1)); +} + +static inline size_t ALIGN_DOWN(size_t l, size_t ali) { + assert(ISPOWEROF2(ali)); + + return l & ~(ali - 1); +} + +static inline uint64_t ALIGN_DOWN_U64(uint64_t l, uint64_t ali) { + assert(ISPOWEROF2(ali)); + + return l & ~(ali - 1); +} + +static inline size_t ALIGN_OFFSET(size_t l, size_t ali) { + assert(ISPOWEROF2(ali)); + + return l & (ali - 1); +} + +static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) { + assert(ISPOWEROF2(ali)); + + return l & (ali - 1); +} + +#define ALIGN2(l) ALIGN_TO(l, 2) +#define ALIGN4(l) ALIGN_TO(l, 4) +#define ALIGN8(l) ALIGN_TO(l, 8) +#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p)) +#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p)) +#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p)) +#define ALIGN(l) ALIGN_TO(l, sizeof(void*)) +#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p))) + +/* Checks if the specified pointer is aligned as appropriate for the specific type */ +#define IS_ALIGNED16(p) (((uintptr_t) p) % alignof(uint16_t) == 0) +#define IS_ALIGNED32(p) (((uintptr_t) p) % alignof(uint32_t) == 0) +#define IS_ALIGNED64(p) (((uintptr_t) p) % alignof(uint64_t) == 0) + +/* Same as ALIGN_TO but callable in constant contexts. */ +#define CONST_ALIGN_TO(l, ali) \ + __builtin_choose_expr( \ + __builtin_constant_p(l) && \ + __builtin_constant_p(ali) && \ + CONST_ISPOWEROF2(ali) && \ + (l <= SIZE_MAX - (ali - 1)), /* overflow? */ \ + ((l) + (ali) - 1) & ~((ali) - 1), \ + VOID_0) + +/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable + * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a + * warning or if you want to assert that the cast gives a pointer of suitable alignment. */ +#define CAST_ALIGN_PTR(t, p) \ + ({ \ + const void *_p = (p); \ + assert(((uintptr_t) _p) % alignof(t) == 0); \ + (t *) _p; \ + }) + +#define UPDATE_FLAG(orig, flag, b) \ + ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) +#define SET_FLAG(v, flag, b) \ + (v) = UPDATE_FLAG(v, flag, b) +#define FLAGS_SET(v, flags) \ + ((~(v) & (flags)) == 0) + +/* A wrapper for 'func' to return void. + * Only useful when a void-returning function is required by some API. */ +#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \ + static inline void name(type *p) { \ + func(p); \ + } + +/* When func() returns the void value (NULL, -1, …) of the appropriate type */ +#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ + static inline void func##p(type *p) { \ + if (*p) \ + *p = func(*p); \ + } + +/* When func() doesn't return the appropriate type, set variable to empty afterwards. + * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */ +#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \ + static inline void func##p(type *p) { \ + if (*p != (empty)) { \ + DISABLE_WARNING_ADDRESS; \ + assert(func); \ + REENABLE_WARNING; \ + func(*p); \ + *p = (empty); \ + } \ + } + +/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */ +#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty) \ + static inline void func##p(type *p) { \ + if (*p != (empty)) { \ + func(*p); \ + *p = (empty); \ + } \ + } + +/* Declare a flexible array usable in a union. + * This is essentially a work-around for a pointless constraint in C99 + * and might go away in some future version of the standard. + * + * See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=3080ea5553cc909b000d1f1d964a9041962f2c5b + */ +#define DECLARE_FLEX_ARRAY(type, name) \ + struct { \ + dummy_t __empty__ ## name; \ + type name[]; \ + } + +/* Declares an ELF read-only string section that does not occupy memory at runtime. */ +#define DECLARE_NOALLOC_SECTION(name, text) \ + asm(".pushsection " name ",\"S\"\n\t" \ + ".ascii " STRINGIFY(text) "\n\t" \ + ".zero 1\n\t" \ + ".popsection\n") + +#ifdef SBAT_DISTRO + #define DECLARE_SBAT(text) DECLARE_NOALLOC_SECTION(".sbat", text) +#else + #define DECLARE_SBAT(text) +#endif diff --git a/src/fundamental/memory-util-fundamental.h b/src/fundamental/memory-util-fundamental.h new file mode 100644 index 0000000..6870f54 --- /dev/null +++ b/src/fundamental/memory-util-fundamental.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stddef.h> + +#if SD_BOOT +# include "efi-string.h" +#else +# include <string.h> +#endif + +#include "macro-fundamental.h" + +#define memzero(x, l) \ + ({ \ + size_t _l_ = (l); \ + _l_ > 0 ? memset((x), 0, _l_) : (x); \ + }) + +#if !SD_BOOT && HAVE_EXPLICIT_BZERO +static inline void *explicit_bzero_safe(void *p, size_t l) { + if (p && l > 0) + explicit_bzero(p, l); + + return p; +} +#else +static inline void *explicit_bzero_safe(void *p, size_t l) { + if (p && l > 0) { + memset(p, 0, l); + __asm__ __volatile__("" : : "r"(p) : "memory"); + } + return p; +} +#endif + +struct VarEraser { + /* NB: This is a pointer to memory to erase in case of CLEANUP_ERASE(). Pointer to pointer to memory + * to erase in case of CLEANUP_ERASE_PTR() */ + void *p; + size_t size; +}; + +static inline void erase_var(struct VarEraser *e) { + explicit_bzero_safe(e->p, e->size); +} + +/* Mark var to be erased when leaving scope. */ +#define CLEANUP_ERASE(var) \ + _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \ + .p = &(var), \ + .size = sizeof(var), \ + } + +static inline void erase_varp(struct VarEraser *e) { + + /* Very similar to erase_var(), but assumes `p` is a pointer to a pointer whose memory shall be destructed. */ + if (!e->p) + return; + + explicit_bzero_safe(*(void**) e->p, e->size); +} + +/* Mark pointer so that memory pointed to is erased when leaving scope. Note: this takes a pointer to the + * specified pointer, instead of just a copy of it. This is to allow callers to invalidate the pointer after + * use, if they like, disabling our automatic erasure (for example because they succeeded with whatever they + * wanted to do and now intend to return the allocated buffer to their caller without it being erased). */ +#define CLEANUP_ERASE_PTR(ptr, sz) \ + _cleanup_(erase_varp) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \ + .p = (ptr), \ + .size = (sz), \ + } + +typedef void (*free_array_func_t)(void *p, size_t n); + +/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */ +typedef struct ArrayCleanup { + void **parray; + size_t *pn; + free_array_func_t pfunc; +} ArrayCleanup; + +static inline void array_cleanup(const ArrayCleanup *c) { + assert(c); + + assert(!c->parray == !c->pn); + + if (!c->parray) + return; + + if (*c->parray) { + assert(c->pfunc); + c->pfunc(*c->parray, *c->pn); + *c->parray = NULL; + } + + *c->pn = 0; +} + +#define CLEANUP_ARRAY(array, n, func) \ + _cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \ + .parray = (void**) &(array), \ + .pn = &(n), \ + .pfunc = (free_array_func_t) ({ \ + void (*_f)(typeof(array[0]) *a, size_t b) = func; \ + _f; \ + }), \ + } diff --git a/src/fundamental/meson.build b/src/fundamental/meson.build new file mode 100644 index 0000000..b7ca6cf --- /dev/null +++ b/src/fundamental/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +fundamental_include = include_directories('.') + +fundamental_sources = files( + 'bootspec-fundamental.c', + 'efivars-fundamental.c', + 'sha256.c', + 'string-util-fundamental.c', + 'uki.c', +) diff --git a/src/fundamental/sbat.h b/src/fundamental/sbat.h new file mode 100644 index 0000000..9288e05 --- /dev/null +++ b/src/fundamental/sbat.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifdef SBAT_DISTRO +# include "version.h" +# define SBAT_MAGIC "sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\n" +# define SBAT_BOOT_SECTION_TEXT \ + SBAT_MAGIC \ + SBAT_PROJECT "-boot" ",1,The systemd Developers," SBAT_PROJECT "," PROJECT_VERSION "," PROJECT_URL "\n" \ + SBAT_PROJECT "-boot" "." SBAT_DISTRO "," STRINGIFY(SBAT_DISTRO_GENERATION) "," SBAT_DISTRO_SUMMARY "," SBAT_DISTRO_PKGNAME "," SBAT_DISTRO_VERSION "," SBAT_DISTRO_URL "\n" +# define SBAT_STUB_SECTION_TEXT \ + SBAT_MAGIC \ + SBAT_PROJECT "-stub" ",1,The systemd Developers," SBAT_PROJECT "," PROJECT_VERSION "," PROJECT_URL "\n" \ + SBAT_PROJECT "-stub" "." SBAT_DISTRO "," STRINGIFY(SBAT_DISTRO_GENERATION) "," SBAT_DISTRO_SUMMARY "," SBAT_DISTRO_PKGNAME "," SBAT_DISTRO_VERSION "," SBAT_DISTRO_URL "\n" +#endif diff --git a/src/fundamental/sha256.c b/src/fundamental/sha256.c new file mode 100644 index 0000000..4389e9e --- /dev/null +++ b/src/fundamental/sha256.c @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +/* Stolen from glibc and converted to our style. In glibc it comes with the following copyright blurb: */ + +/* Functions to compute SHA256 message digest of files or memory blocks. + according to the definition of SHA256 in FIPS 180-2. + Copyright (C) 2007-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <stdbool.h> +#if SD_BOOT +# include "efi-string.h" +#else +# include <string.h> +#endif + +#include "macro-fundamental.h" +#include "sha256.h" +#include "unaligned-fundamental.h" + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +# define SWAP64(n) \ + (((n) << 56) \ + | (((n) & 0xff00) << 40) \ + | (((n) & 0xff0000) << 24) \ + | (((n) & 0xff000000) << 8) \ + | (((n) >> 8) & 0xff000000) \ + | (((n) >> 24) & 0xff0000) \ + | (((n) >> 40) & 0xff00) \ + | ((n) >> 56)) +#else +# define SWAP(n) (n) +# define SWAP64(n) (n) +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (FIPS 180-2:5.1.1) */ +static const uint8_t fillbuf[64] = { + 0x80, 0 /* , 0, 0, ... */ +}; + +/* Constants for SHA256 from FIPS 180-2:4.2.2. */ +static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static void sha256_process_block(const void *, size_t, struct sha256_ctx *); + +/* Initialize structure containing state of computation. + (FIPS 180-2:5.3.2) */ +void sha256_init_ctx(struct sha256_ctx *ctx) { + assert(ctx); + + ctx->H[0] = 0x6a09e667; + ctx->H[1] = 0xbb67ae85; + ctx->H[2] = 0x3c6ef372; + ctx->H[3] = 0xa54ff53a; + ctx->H[4] = 0x510e527f; + ctx->H[5] = 0x9b05688c; + ctx->H[6] = 0x1f83d9ab; + ctx->H[7] = 0x5be0cd19; + + ctx->total64 = 0; + ctx->buflen = 0; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. */ +uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]) { + /* Take yet unprocessed bytes into account. */ + uint32_t bytes = ctx->buflen; + size_t pad; + + assert(ctx); + assert(resbuf); + + /* Now count remaining bytes. */ + ctx->total64 += bytes; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy(&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->buffer32[(bytes + pad + 4) / 4] = SWAP(ctx->total[TOTAL64_low] << 3); + ctx->buffer32[(bytes + pad) / 4] = SWAP((ctx->total[TOTAL64_high] << 3) + | (ctx->total[TOTAL64_low] >> 29)); + + /* Process last bytes. */ + sha256_process_block(ctx->buffer, bytes + pad + 8, ctx); + + /* Put result from CTX in first 32 bytes following RESBUF. */ + for (size_t i = 0; i < 8; ++i) + unaligned_write_ne32(resbuf + i * sizeof(uint32_t), SWAP(ctx->H[i])); + return resbuf; +} + +void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx) { + assert(buffer); + assert(ctx); + + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + + if (ctx->buflen != 0) { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy(&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) { + sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) { + if (IS_ALIGNED32(buffer)) { + sha256_process_block(buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } else + while (len > 64) { + memcpy(ctx->buffer, buffer, 64); + sha256_process_block(ctx->buffer, 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + } + + /* Move remaining bytes into internal buffer. */ + if (len > 0) { + size_t left_over = ctx->buflen; + + memcpy(&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) { + sha256_process_block(ctx->buffer, 64, ctx); + left_over -= 64; + memcpy(ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ +static void sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx) { + const uint32_t *words = ASSERT_PTR(buffer); + size_t nwords = len / sizeof(uint32_t); + + assert(ctx); + + uint32_t a = ctx->H[0]; + uint32_t b = ctx->H[1]; + uint32_t c = ctx->H[2]; + uint32_t d = ctx->H[3]; + uint32_t e = ctx->H[4]; + uint32_t f = ctx->H[5]; + uint32_t g = ctx->H[6]; + uint32_t h = ctx->H[7]; + + /* First increment the byte count. FIPS 180-2 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. */ + ctx->total64 += len; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (nwords > 0) { + uint32_t W[64]; + uint32_t a_save = a; + uint32_t b_save = b; + uint32_t c_save = c; + uint32_t d_save = d; + uint32_t e_save = e; + uint32_t f_save = f; + uint32_t g_save = g; + uint32_t h_save = h; + + /* Operators defined in FIPS 180-2:4.1.2. */ +#define Ch(x, y, z) ((x & y) ^ (~x & z)) +#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) +#define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22)) +#define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25)) +#define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3)) +#define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10)) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) ((w >> s) | (w << (32 - s))) + + /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */ + for (size_t t = 0; t < 16; ++t) { + W[t] = SWAP (*words); + ++words; + } + for (size_t t = 16; t < 64; ++t) + W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16]; + + /* The actual computation according to FIPS 180-2:6.2.2 step 3. */ + for (size_t t = 0; t < 64; ++t) { + uint32_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t]; + uint32_t T2 = S0 (a) + Maj (a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + /* Add the starting values of the context according to FIPS 180-2:6.2.2 + step 4. */ + a += a_save; + b += b_save; + c += c_save; + d += d_save; + e += e_save; + f += f_save; + g += g_save; + h += h_save; + + /* Prepare for the next round. */ + nwords -= 16; + } + + /* Put checksum in context given as argument. */ + ctx->H[0] = a; + ctx->H[1] = b; + ctx->H[2] = c; + ctx->H[3] = d; + ctx->H[4] = e; + ctx->H[5] = f; + ctx->H[6] = g; + ctx->H[7] = h; +} + +uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]) { + struct sha256_ctx ctx; + sha256_init_ctx(&ctx); + sha256_process_bytes(buffer, sz, &ctx); + return sha256_finish_ctx(&ctx, result); +} diff --git a/src/fundamental/sha256.h b/src/fundamental/sha256.h new file mode 100644 index 0000000..dbb08e3 --- /dev/null +++ b/src/fundamental/sha256.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stddef.h> +#include <stdint.h> + +#define SHA256_DIGEST_SIZE 32 + +struct sha256_ctx { + uint32_t H[8]; + + union { + uint64_t total64; +#define TOTAL64_low (1 - (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#define TOTAL64_high (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + uint32_t total[2]; + }; + + uint32_t buflen; + + union { + uint8_t buffer[128]; /* NB: always correctly aligned for UINT32. */ + uint32_t buffer32[32]; + uint64_t buffer64[16]; + }; +}; + +void sha256_init_ctx(struct sha256_ctx *ctx); +uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]); +void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx); + +static inline void sha256_process_bytes_and_size(const void *buffer, size_t len, struct sha256_ctx *ctx) { + sha256_process_bytes(&len, sizeof(len), ctx); + sha256_process_bytes(buffer, len, ctx); +} + +uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]); + +#define SHA256_DIRECT(buffer, sz) sha256_direct(buffer, sz, (uint8_t[SHA256_DIGEST_SIZE]) {}) diff --git a/src/fundamental/string-util-fundamental.c b/src/fundamental/string-util-fundamental.c new file mode 100644 index 0000000..a5bafc6 --- /dev/null +++ b/src/fundamental/string-util-fundamental.c @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#if !SD_BOOT +# include <ctype.h> +#endif + +#include "macro-fundamental.h" +#include "string-util-fundamental.h" + +sd_char *startswith(const sd_char *s, const sd_char *prefix) { + size_t l; + + assert(s); + assert(prefix); + + l = strlen(prefix); + if (!strneq(s, prefix, l)) + return NULL; + + return (sd_char*) s + l; +} + +sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) { + size_t l; + + assert(s); + assert(prefix); + + l = strlen(prefix); + if (!strncaseeq(s, prefix, l)) + return NULL; + + return (sd_char*) s + l; +} + +sd_char* endswith(const sd_char *s, const sd_char *postfix) { + size_t sl, pl; + + assert(s); + assert(postfix); + + sl = strlen(s); + pl = strlen(postfix); + + if (pl == 0) + return (sd_char*) s + sl; + + if (sl < pl) + return NULL; + + if (strcmp(s + sl - pl, postfix) != 0) + return NULL; + + return (sd_char*) s + sl - pl; +} + +sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) { + size_t sl, pl; + + assert(s); + assert(postfix); + + sl = strlen(s); + pl = strlen(postfix); + + if (pl == 0) + return (sd_char*) s + sl; + + if (sl < pl) + return NULL; + + if (strcasecmp(s + sl - pl, postfix) != 0) + return NULL; + + return (sd_char*) s + sl - pl; +} + +static bool is_valid_version_char(sd_char a) { + return ascii_isdigit(a) || ascii_isalpha(a) || IN_SET(a, '~', '-', '^', '.'); +} + +int strverscmp_improved(const sd_char *a, const sd_char *b) { + /* This function is similar to strverscmp(3), but it treats '-' and '.' as separators. + * + * The logic is based on rpm's rpmvercmp(), but unlike rpmvercmp(), it distiguishes e.g. + * '123a' and '123.a', with '123a' being newer. + * + * It allows direct comparison of strings which contain both a version and a release; e.g. + * '247.2-3.1.fc33.x86_64' or '5.11.0-0.rc5.20210128git76c057c84d28.137.fc34'. + * + * The input string is split into segments. Each segment is numeric or alphabetic, and may be + * prefixed with the following: + * '~' : used for pre-releases, a segment prefixed with this is the oldest, + * '-' : used for the separator between version and release, + * '^' : used for patched releases, a segment with this is newer than one with '-'. + * '.' : used for point releases. + * Note that no prefix segment is the newest. All non-supported characters are dropped, and + * handled as a separator of segments, e.g., '123_a' is equivalent to '123a'. + * + * By using this, version strings can be sorted like following: + * (older) 122.1 + * ^ 123~rc1-1 + * | 123 + * | 123-a + * | 123-a.1 + * | 123-1 + * | 123-1.1 + * | 123^post1 + * | 123.a-1 + * | 123.1-1 + * v 123a-1 + * (newer) 124-1 + */ + + a = strempty(a); + b = strempty(b); + + for (;;) { + const sd_char *aa, *bb; + int r; + + /* Drop leading invalid characters. */ + while (*a != '\0' && !is_valid_version_char(*a)) + a++; + while (*b != '\0' && !is_valid_version_char(*b)) + b++; + + /* Handle '~'. Used for pre-releases, e.g. 123~rc1, or 4.5~alpha1 */ + if (*a == '~' || *b == '~') { + /* The string prefixed with '~' is older. */ + r = CMP(*a != '~', *b != '~'); + if (r != 0) + return r; + + /* Now both strings are prefixed with '~'. Compare remaining strings. */ + a++; + b++; + } + + /* If at least one string reaches the end, then longer is newer. + * Note that except for '~' prefixed segments, a string which has more segments is newer. + * So, this check must be after the '~' check. */ + if (*a == '\0' || *b == '\0') + return CMP(*a, *b); + + /* Handle '-', which separates version and release, e.g 123.4-3.1.fc33.x86_64 */ + if (*a == '-' || *b == '-') { + /* The string prefixed with '-' is older (e.g., 123-9 vs 123.1-1) */ + r = CMP(*a != '-', *b != '-'); + if (r != 0) + return r; + + a++; + b++; + } + + /* Handle '^'. Used for patched release. */ + if (*a == '^' || *b == '^') { + r = CMP(*a != '^', *b != '^'); + if (r != 0) + return r; + + a++; + b++; + } + + /* Handle '.'. Used for point releases. */ + if (*a == '.' || *b == '.') { + r = CMP(*a != '.', *b != '.'); + if (r != 0) + return r; + + a++; + b++; + } + + if (ascii_isdigit(*a) || ascii_isdigit(*b)) { + /* Find the leading numeric segments. One may be an empty string. So, + * numeric segments are always newer than alpha segments. */ + for (aa = a; ascii_isdigit(*aa); aa++) + ; + for (bb = b; ascii_isdigit(*bb); bb++) + ; + + /* Check if one of the strings was empty, but the other not. */ + r = CMP(a != aa, b != bb); + if (r != 0) + return r; + + /* Skip leading '0', to make 00123 equivalent to 123. */ + while (*a == '0') + a++; + while (*b == '0') + b++; + + /* To compare numeric segments without parsing their values, first compare the + * lengths of the segments. Eg. 12345 vs 123, longer is newer. */ + r = CMP(aa - a, bb - b); + if (r != 0) + return r; + + /* Then, compare them as strings. */ + r = CMP(strncmp(a, b, aa - a), 0); + if (r != 0) + return r; + } else { + /* Find the leading non-numeric segments. */ + for (aa = a; ascii_isalpha(*aa); aa++) + ; + for (bb = b; ascii_isalpha(*bb); bb++) + ; + + /* Note that the segments are usually not NUL-terminated. */ + r = CMP(strncmp(a, b, MIN(aa - a, bb - b)), 0); + if (r != 0) + return r; + + /* Longer is newer, e.g. abc vs abcde. */ + r = CMP(aa - a, bb - b); + if (r != 0) + return r; + } + + /* The current segments are equivalent. Let's move to the next one. */ + a = aa; + b = bb; + } +} diff --git a/src/fundamental/string-util-fundamental.h b/src/fundamental/string-util-fundamental.h new file mode 100644 index 0000000..b537b2e --- /dev/null +++ b/src/fundamental/string-util-fundamental.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#if SD_BOOT +# include "efi.h" +# include "efi-string.h" +#else +# include <string.h> +#endif + +#include "macro-fundamental.h" + +#if SD_BOOT +# define strlen strlen16 +# define strcmp strcmp16 +# define strncmp strncmp16 +# define strcasecmp strcasecmp16 +# define strncasecmp strncasecmp16 +# define STR_C(str) (L ## str) +typedef char16_t sd_char; +#else +# define STR_C(str) (str) +typedef char sd_char; +#endif + +#define streq(a,b) (strcmp((a),(b)) == 0) +#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) +#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) +#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) + +static inline int strcmp_ptr(const sd_char *a, const sd_char *b) { + if (a && b) + return strcmp(a, b); + + return CMP(a, b); +} + +static inline int strcasecmp_ptr(const sd_char *a, const sd_char *b) { + if (a && b) + return strcasecmp(a, b); + + return CMP(a, b); +} + +static inline bool streq_ptr(const sd_char *a, const sd_char *b) { + return strcmp_ptr(a, b) == 0; +} + +static inline bool strcaseeq_ptr(const sd_char *a, const sd_char *b) { + return strcasecmp_ptr(a, b) == 0; +} + +static inline size_t strlen_ptr(const sd_char *s) { + if (!s) + return 0; + + return strlen(s); +} + +sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_; +sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_; +sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_; +sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_; + +static inline bool isempty(const sd_char *a) { + return !a || a[0] == '\0'; +} + +static inline const sd_char *strempty(const sd_char *s) { + return s ?: STR_C(""); +} + +static inline const sd_char *yes_no(bool b) { + return b ? STR_C("yes") : STR_C("no"); +} + +static inline const sd_char *on_off(bool b) { + return b ? STR_C("on") : STR_C("off"); +} + +static inline const sd_char* comparison_operator(int result) { + return result < 0 ? STR_C("<") : result > 0 ? STR_C(">") : STR_C("=="); +} + +int strverscmp_improved(const sd_char *a, const sd_char *b); + +/* Like startswith(), but operates on arbitrary memory blocks */ +static inline void *memory_startswith(const void *p, size_t sz, const sd_char *token) { + assert(token); + + size_t n = strlen(token) * sizeof(sd_char); + if (sz < n) + return NULL; + + assert(p); + + if (memcmp(p, token, n) != 0) + return NULL; + + return (uint8_t*) p + n; +} + +#define _STRV_FOREACH(s, l, i) \ + for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++) + +#define STRV_FOREACH(s, l) \ + _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ)) + +static inline bool ascii_isdigit(sd_char a) { + /* A pure ASCII, locale independent version of isdigit() */ + return a >= '0' && a <= '9'; +} + +static inline bool ascii_ishex(sd_char a) { + return ascii_isdigit(a) || (a >= 'a' && a <= 'f') || (a >= 'A' && a <= 'F'); +} + +static inline bool ascii_isalpha(sd_char a) { + /* A pure ASCII, locale independent version of isalpha() */ + return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); +} diff --git a/src/fundamental/tpm2-pcr.h b/src/fundamental/tpm2-pcr.h new file mode 100644 index 0000000..d0d5b74 --- /dev/null +++ b/src/fundamental/tpm2-pcr.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "macro-fundamental.h" + +/* The various TPM PCRs we measure into from sd-stub and sd-boot. */ + +enum { + /* The following names for PCRs 0…7 are based on the names in the "TCG PC Client Specific Platform + * Firmware Profile Specification" + * (https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/) */ + TPM2_PCR_PLATFORM_CODE = 0, + TPM2_PCR_PLATFORM_CONFIG = 1, + TPM2_PCR_EXTERNAL_CODE = 2, + TPM2_PCR_EXTERNAL_CONFIG = 3, + TPM2_PCR_BOOT_LOADER_CODE = 4, + TPM2_PCR_BOOT_LOADER_CONFIG = 5, + TPM2_PCR_HOST_PLATFORM = 6, + TPM2_PCR_SECURE_BOOT_POLICY = 7, + + /* The following names for PCRs 9…15 are based on the "Linux TPM PCR Registry" + (https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/) */ + TPM2_PCR_KERNEL_INITRD = 9, + TPM2_PCR_IMA = 10, + + /* systemd: This TPM PCR is where we extend the sd-stub "payloads" into, before using them. i.e. the kernel + * ELF image, embedded initrd, and so on. In contrast to PCR 4 (which also contains this data, given + * the whole surrounding PE image is measured into it) this should be reasonably pre-calculatable, + * because it *only* consists of static data from the kernel PE image. */ + TPM2_PCR_KERNEL_BOOT = 11, + + /* systemd: This TPM PCR is where sd-stub extends the kernel command line and any passed credentials into. */ + TPM2_PCR_KERNEL_CONFIG = 12, + + /* systemd: This TPM PCR is where we extend the initrd sysext images into which we pass to the booted kernel */ + TPM2_PCR_SYSEXTS = 13, + TPM2_PCR_SHIM_POLICY = 14, + + /* systemd: This TPM PCR is where we measure the root fs volume key (and maybe /var/'s) if it is split off */ + TPM2_PCR_SYSTEM_IDENTITY = 15, + + /* As per "TCG PC Client Specific Platform Firmware Profile Specification" again, see above */ + TPM2_PCR_DEBUG = 16, + TPM2_PCR_APPLICATION_SUPPORT = 23, +}; + +/* The tag used for EV_EVENT_TAG event log records covering the boot loader config */ +#define LOADER_CONF_EVENT_TAG_ID UINT32_C(0xf5bc582a) + +/* The tag used for EV_EVENT_TAG event log records covering Devicetree blobs */ +#define DEVICETREE_ADDON_EVENT_TAG_ID UINT32_C(0x6c46f751) diff --git a/src/fundamental/uki.c b/src/fundamental/uki.c new file mode 100644 index 0000000..b1fa044 --- /dev/null +++ b/src/fundamental/uki.c @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <stddef.h> + +#include "uki.h" + +const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = { + /* These section names must fit in 8ch (excluding any trailing NUL) as per PE spec for executables: + * https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers + * (Note that PE *object* files may have longer section names (via indirection in the string table) but + * this is not allowed for PE *executables*, which UKIs are.) */ + [UNIFIED_SECTION_LINUX] = ".linux", + [UNIFIED_SECTION_OSREL] = ".osrel", + [UNIFIED_SECTION_CMDLINE] = ".cmdline", + [UNIFIED_SECTION_INITRD] = ".initrd", + [UNIFIED_SECTION_SPLASH] = ".splash", + [UNIFIED_SECTION_DTB] = ".dtb", + [UNIFIED_SECTION_UNAME] = ".uname", + [UNIFIED_SECTION_SBAT] = ".sbat", + [UNIFIED_SECTION_PCRSIG] = ".pcrsig", + [UNIFIED_SECTION_PCRPKEY] = ".pcrpkey", + NULL, +}; diff --git a/src/fundamental/uki.h b/src/fundamental/uki.h new file mode 100644 index 0000000..ffa960f --- /dev/null +++ b/src/fundamental/uki.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "macro-fundamental.h" + +/* List of PE sections that have special meaning for us in unified kernels. This is the canonical order in + * which we measure the sections into TPM PCR 11. PLEASE DO NOT REORDER! */ +typedef enum UnifiedSection { + UNIFIED_SECTION_LINUX, + UNIFIED_SECTION_OSREL, + UNIFIED_SECTION_CMDLINE, + UNIFIED_SECTION_INITRD, + UNIFIED_SECTION_SPLASH, + UNIFIED_SECTION_DTB, + UNIFIED_SECTION_UNAME, + UNIFIED_SECTION_SBAT, + UNIFIED_SECTION_PCRSIG, + UNIFIED_SECTION_PCRPKEY, + _UNIFIED_SECTION_MAX, +} UnifiedSection; + +extern const char* const unified_sections[_UNIFIED_SECTION_MAX + 1]; + +static inline bool unified_section_measure(UnifiedSection section) { + /* Don't include the PCR signature in the PCR measurements, since they sign the expected result of + * the measurement, and hence shouldn't be input to it. */ + return section >= 0 && section < _UNIFIED_SECTION_MAX && section != UNIFIED_SECTION_PCRSIG; +} diff --git a/src/fundamental/unaligned-fundamental.h b/src/fundamental/unaligned-fundamental.h new file mode 100644 index 0000000..a4c810a --- /dev/null +++ b/src/fundamental/unaligned-fundamental.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdint.h> + +static inline uint16_t unaligned_read_ne16(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; + + return u->x; +} + +static inline uint32_t unaligned_read_ne32(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; + + return u->x; +} + +static inline uint64_t unaligned_read_ne64(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; + + return u->x; +} + +static inline void unaligned_write_ne16(void *_u, uint16_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; + + u->x = a; +} + +static inline void unaligned_write_ne32(void *_u, uint32_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; + + u->x = a; +} + +static inline void unaligned_write_ne64(void *_u, uint64_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; + + u->x = a; +} |