]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/efi-loader.c
Merge pull request #31524 from poettering/secure-getenv-naming-fix
[thirdparty/systemd.git] / src / shared / efi-loader.c
index e114d5ea2c60af0d3a4c686f59bfb31ad200c272..ab377aaa8b7dff2217482e7f05dd95b5a8192442 100644 (file)
@@ -1,11 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "alloc-util.h"
+#include "efi-api.h"
 #include "efi-loader.h"
+#include "env-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "stat-util.h"
 #include "strv.h"
+#include "tpm2-pcr.h"
 #include "utf8.h"
 
 #if ENABLE_EFI
@@ -60,7 +63,8 @@ int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
 
 int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
         _cleanup_free_ char *p = NULL;
-        int r, parsed[16];
+        int r;
+        unsigned parsed[16];
 
         if (!is_efi_boot())
                 return -EOPNOTSUPP;
@@ -98,7 +102,8 @@ int efi_loader_get_entries(char ***ret) {
         if (r < 0)
                 return r;
 
-        /* The variable contains a series of individually NUL terminated UTF-16 strings. */
+        /* The variable contains a series of individually NUL terminated UTF-16 strings. We gracefully
+         * consider the final NUL byte optional (i.e. the last string may or may not end in a NUL byte).*/
 
         for (size_t i = 0, start = 0;; i++) {
                 _cleanup_free_ char *decoded = NULL;
@@ -112,6 +117,11 @@ int efi_loader_get_entries(char ***ret) {
                 if (!end && entries[i] != 0)
                         continue;
 
+                /* Empty string at the end of variable? That's the trailer, we are done (i.e. we have a final
+                 * NUL terminator). */
+                if (end && start == i)
+                        break;
+
                 /* We reached the end of a string, let's decode it into UTF-8 */
                 decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
                 if (!decoded)
@@ -124,7 +134,8 @@ int efi_loader_get_entries(char ***ret) {
                 } else
                         log_debug("Ignoring invalid loader entry '%s'.", decoded);
 
-                /* We reached the end of the variable */
+                /* Exit the loop if we reached the end of the variable (i.e. we do not have a final NUL
+                 * terminator) */
                 if (end)
                         break;
 
@@ -187,6 +198,99 @@ int efi_loader_get_features(uint64_t *ret) {
         return 0;
 }
 
+int efi_stub_get_features(uint64_t *ret) {
+        _cleanup_free_ void *v = NULL;
+        size_t s;
+        int r;
+
+        assert(ret);
+
+        if (!is_efi_boot()) {
+                *ret = 0;
+                return 0;
+        }
+
+        r = efi_get_variable(EFI_LOADER_VARIABLE(StubFeatures), NULL, &v, &s);
+        if (r == -ENOENT) {
+                _cleanup_free_ char *info = NULL;
+
+                /* The new (v252+) StubFeatures variable is not supported, let's see if it's systemd-stub at all */
+                r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubInfo), &info);
+                if (r < 0) {
+                        if (r != -ENOENT)
+                                return r;
+
+                        /* Variable not set, definitely means not systemd-stub */
+
+                } else if (first_word(info, "systemd-stub")) {
+
+                        /* An older systemd-stub version. Let's hardcode the feature set, since it was pretty
+                         * static in all its versions. */
+
+                        *ret = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION;
+                        return 0;
+                }
+
+                /* No features supported */
+                *ret = 0;
+                return 0;
+        }
+        if (r < 0)
+                return r;
+
+        if (s != sizeof(uint64_t))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "StubFeatures EFI variable doesn't have the right size.");
+
+        memcpy(ret, v, sizeof(uint64_t));
+        return 0;
+}
+
+int efi_measured_uki(int log_level) {
+        _cleanup_free_ char *pcr_string = NULL;
+        static int cached = -1;
+        unsigned pcr_nr;
+        int r;
+
+        if (cached >= 0)
+                return cached;
+
+        /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11 on a TPM2
+         * chip. Or in other words, if we are running on a TPM enabled UKI. (TPM 1.2 situations are ignored.)
+         *
+         * Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
+         * being used, but it measured things into a different PCR than we are configured for in
+         * userspace. (i.e. we expect PCR 11 being used for this by both sd-stub and us) */
+
+        r = secure_getenv_bool("SYSTEMD_FORCE_MEASURE"); /* Give user a chance to override the variable test,
+                                                          * for debugging purposes */
+        if (r >= 0)
+                return (cached = r);
+        if (r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
+
+        if (!efi_has_tpm2())
+                return (cached = 0);
+
+        r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
+        if (r == -ENOENT)
+                return (cached = 0);
+        if (r < 0)
+                return log_full_errno(log_level, r,
+                                      "Failed to get StubPcrKernelImage EFI variable: %m");
+
+        r = safe_atou(pcr_string, &pcr_nr);
+        if (r < 0)
+                return log_full_errno(log_level, r,
+                                      "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
+        if (pcr_nr != TPM2_PCR_KERNEL_BOOT)
+                return log_full_errno(log_level, SYNTHETIC_ERRNO(EREMOTE),
+                                      "Kernel stub measured kernel image into PCR %u, which is different than expected %i.",
+                                      pcr_nr, TPM2_PCR_KERNEL_BOOT);
+
+        return (cached = 1);
+}
+
 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
         _cleanup_free_ char *v = NULL;
         static struct stat cache_stat = {};
@@ -217,7 +321,7 @@ int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
                 return -ERANGE;
 
         cache_stat = new_stat;
-        *ret = cache = sec * USEC_PER_SEC; /* return in µs */
+        *ret = cache = sec * USEC_PER_SEC; /* return in μs */
         return 0;
 }