Package: systemd
Version: 257.5-2
Severity: minor
Tags: patch

Dear developers,

TL;DR postinst should remove its previous boot entry when first installed, install mm alongside shim and fb in the default boot path, and either not use `--make-machine-id-directory=auto` or fix `$ESP/loader/loader.conf`

Long version:

The current systemd-boot maintainer scripts work pretty well for the vast majority of cases, but may fail to do the right thing in some rare scenarios (hence the "Severity: minor" tag).

Please note: all the facts described here were tested on a Bookworm install, with systemd-boot from backports, and homemade helper packages to mimic the current systemd-boot maintainer scripts and sign systemd-boot with a MOK, but in theory the errors described here would also apply to Trixie, and should IMHO be fixed.

Here is the faulty scenario: let's say debian-installer is customized to not install GRUB, install systemd-boot, generate a MOK, sign systemd-boot with it (on Bookworm), and generate UKIs also signed by the MOK, all before first boot. The MOK is imported, the system boots for the first time, shim chainloads systemd-boot and everything works perfectly well as intended.

Now, let's say that the machine must be reinstalled for some reason, and the same procedure runs again, without wiping the boot entries and/or ESP.

Here's what happens during the second install:
- sd-boot postinst runs `bootctl install` again, which blindly adds its boot entry (plain sd-boot without shim) at the top of boot order - postinst checks for shim's boot entry, finds it and does nothing, despite that it's not the default one anymore

Then, on first boot:
- UEFI boots plain sd-boot (without shim), which fails. To fix this, postinst should check if the entry it previously created is still first in boot order, and also if the ESP's GUID did not change. The simplest thing to do is to simply delete the entry when the package is installed for the first time (i.e. if $2 is empty) - UEFI tries the default boot path, but since a MOK import was requested during installation, shim tries to run mm, which fails. To fix this, postinst should also copy mm to the default path, in addition of shim and fb.

After this step, if mm is copied to the default path by other means, UEFI tries again the default boot path, shim runs mm, and the MOK is imported. After a reboot, UEFI again boots plain systemd-boot without shim and fails, and tries again the default boot path, shim then runs fb, which fixes the boot entry to chainload sd-boot through shim.

Finally, after another reboot, UEFI at last loads shim which chainloads systemd-boot, but there's still another problem: if d-i reuses the ESP without formating it, systemd-boot tries to boot the kernels/UKIs from the previous install, due to the presence of a `default` directive in `$ESP/loader/loader.conf`, and since those entries have the `root=` parameter which refers to the old (now missing) partition, this of course fails.

This directive is inserted by `bootctl install` because it's called in the postinst with the undocumented option `--make-machine-id-directory=auto`, **and** systemd-boot is configured to use Type #1 entries (i.e. there's no `/etc/kernel/install.conf` with `layout=uki`, or not yet).

To fix this, either remove this option from the postint and let systemd-boot decide by itself which entry should be considered the default (as intended by upstream), or fix `$ESP/loader/loader.conf` afterwards to set the `default` directive and boot the new kernels/UKIs, which would be consistent with the "take over ESP and EFI boot entry" logic of the postinst.

You'll find attached a series of patches to fix all of those three problems. Patches numbered "0003" are mutually exclusive. I tried to mimic your coding style as much as I could, but they may not be perfect in this regard.

By the way, I won't open a new bug report for that, but when you fixed #1106024, you didn't add code to the `remove_shim` function to remove fb, and since mm would also need this, I also attached a patch to clean up those files.

Regards,

--
Raphaël Halimi
From 2f8c6e0778c4d16a4153c06ada56ed9938c5227a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Halimi?= <raphael.hal...@gmail.com>
Date: Sat, 31 May 2025 19:09:01 +0200
Subject: [PATCH 1/4] Fix boot entry not set ad default

---
 debian/systemd-boot.postinst | 77 ++++++++++++++++--------------------
 1 file changed, 35 insertions(+), 42 deletions(-)

diff --git a/debian/systemd-boot.postinst b/debian/systemd-boot.postinst
index 7b9557bda6..f15014ca9b 100644
--- a/debian/systemd-boot.postinst
+++ b/debian/systemd-boot.postinst
@@ -13,27 +13,32 @@
 
 set -e
 
-remove_shim() {
-    case "$(dpkg --print-architecture)" in
-        amd64)
-        efi_arch_upper=X64
-        efi_arch=x64
-        grub_arch=x86_64
-        ;;
-        arm64)
-        efi_arch_upper=AA64
-        efi_arch=aa64
-        grub_arch=arm64
-        ;;
-        *)
-        return
-    esac
-
-    # shellcheck disable=SC1091
-    . /etc/os-release || . /usr/lib/os-release
-    vendor="${ID:-debian}"
-    vendor_upper="$(echo "$vendor" | cut -c1 | tr '[:lower:]' '[:upper:]')$(echo "$vendor" | cut -c2-)"
+case "$(dpkg --print-architecture)" in
+    amd64)
+    efi_arch_upper=X64
+    efi_arch=x64
+    grub_arch=x86_64
+    ;;
+    arm64)
+    efi_arch_upper=AA64
+    efi_arch=aa64
+    grub_arch=arm64
+    ;;
+    *)
+    return
+esac
+
+# shellcheck disable=SC1091
+. /etc/os-release || . /usr/lib/os-release
+vendor="${ID:-debian}"
+vendor_upper="$(echo "$vendor" | cut -c1 | tr '[:lower:]' '[:upper:]')$(echo "$vendor" | cut -c2-)"
+
+get_boot_entry () {
+    # 730079007300740065006d0064002d0062006f006f007400 is 'systemd-boot' encoded in UTF-16-LE"
+    efibootmgr | grep -i -q "Boot.*${vendor_upper}.*EFI\\\\${vendor}\\\\shim${efi_arch}.efi.*systemd-boot${efi_arch}.efi\|Boot.*${vendor_upper}.*EFI\\\\${vendor}\\\\shim${efi_arch}.efi.*730079007300740065006d0064002d0062006f006f007400" | cut -d' ' -f1 | sed -e 's/Boot//' -e 's/*//'
+}
 
+remove_shim() {
     esp_path="$(bootctl --quiet --print-esp-path 2>/dev/null)"
     if [ -z "$esp_path" ]; then
         return
@@ -65,21 +70,6 @@ remove_shim() {
 }
 
 install_shim() {
-    case "$(dpkg --print-architecture)" in
-        amd64)
-        efi_arch_upper=X64
-        efi_arch=x64
-        grub_arch=x86_64
-        ;;
-        arm64)
-        efi_arch_upper=AA64
-        efi_arch=aa64
-        grub_arch=arm64
-        ;;
-        *)
-        return
-    esac
-
     if [ ! -f "/usr/lib/shim/shim${efi_arch}.efi.signed" ] || [ ! -f "/usr/lib/systemd/boot/efi/systemd-boot${efi_arch}.efi.signed" ]; then
         if [ "$1" = trigger ]; then
             remove_shim
@@ -96,11 +86,6 @@ install_shim() {
         return
     fi
 
-    # shellcheck disable=SC1091
-    . /etc/os-release || . /usr/lib/os-release
-    vendor="${ID:-debian}"
-    vendor_upper="$(echo "$vendor" | cut -c1 | tr '[:lower:]' '[:upper:]')$(echo "$vendor" | cut -c2-)"
-
     for f in shim fb mm; do
         if [ ! -f "/usr/lib/shim/${f}${efi_arch}.efi.signed" ]; then
             continue
@@ -123,10 +108,9 @@ install_shim() {
         printf "shim%s.efi,%s,\\\\EFI\\\\systemd\\\\systemd-boot%s.efi \\\\0,This is the boot entry for %s\n" "${efi_arch}" "${vendor_upper}" "${efi_arch}" "${vendor_upper}" | iconv -t UCS-2LE > "${esp_path}/EFI/${vendor}/BOOT${efi_arch_upper}.CSV"
     fi
 
-    # 730079007300740065006d0064002d0062006f006f007400 is 'systemd-boot' encoded in UTF-16-LE"
     if ! command -v efibootmgr >/dev/null 2>&1; then
         echo "efibootmgr not found, skipping boot entry creation"
-    elif ! efibootmgr | grep -i -q "Boot.*${vendor_upper}.*EFI\\\\${vendor}\\\\shim${efi_arch}.efi.*systemd-boot${efi_arch}.efi\|Boot.*${vendor_upper}.*EFI\\\\${vendor}\\\\shim${efi_arch}.efi.*730079007300740065006d0064002d0062006f006f007400"; then
+    elif [ -z "$(get_boot_entry)" ]; then
         blkpart="$(findmnt -nvo SOURCE "$esp_path")"
         if [ ! -L "/sys/class/block/${blkpart##*/}" ]; then
             return
@@ -139,6 +123,15 @@ install_shim() {
     fi
 }
 
+if [ "$1" = "configure" ] && [ -z "$2" ]; then
+    if command -v efibootmgr >/dev/null 2>&1; then
+        bootentry="$(get_boot_entry)"
+        if [ -n "$bootentry" ]; then
+            efibootmgr -q --delete-bootnum --bootnum "$bootentry"
+        fi
+    fi
+fi
+
 if [ "$1" = configure ] && bootctl --print-esp-path > /dev/null 2>&1; then
     if bootctl is-installed > /dev/null 2>&1; then
         bootctl update --graceful
-- 
2.49.0

From e0b5ac45f28f600c53d1e2bc3ec74d253f304046 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Halimi?= <raphael.hal...@gmail.com>
Date: Sat, 31 May 2025 19:15:36 +0200
Subject: [PATCH 2/4] Copy Mok Manager to default boot path

---
 debian/systemd-boot.postinst | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/debian/systemd-boot.postinst b/debian/systemd-boot.postinst
index f15014ca9b..86d1977b87 100644
--- a/debian/systemd-boot.postinst
+++ b/debian/systemd-boot.postinst
@@ -100,9 +100,11 @@ install_shim() {
         install -p -D "/usr/lib/shim/shim${efi_arch}.efi.signed" "${esp_path}/EFI/BOOT/BOOT${efi_arch_upper}.efi"
     fi
 
-    if [ ! -f "${esp_path}/EFI/BOOT/fb${efi_arch}.efi" ] || [ "$(<"${esp_path}/EFI/BOOT/fb${efi_arch}.efi" sha256sum)" != "$(<"/usr/lib/shim/fb${efi_arch}.efi.signed" sha256sum)" ]; then
-        install -p -D "/usr/lib/shim/fb${efi_arch}.efi.signed" "${esp_path}/EFI/BOOT/fb${efi_arch}.efi"
-    fi
+    for f in fb mm; do
+        if [ ! -f "${esp_path}/EFI/BOOT/${f}${efi_arch}.efi" ] || [ "$(<"${esp_path}/EFI/BOOT/${f}${efi_arch}.efi" sha256sum)" != "$(<"/usr/lib/shim/${f}${efi_arch}.efi.signed" sha256sum)" ]; then
+            install -p -D "/usr/lib/shim/${f}${efi_arch}.efi.signed" "${esp_path}/EFI/BOOT/${f}${efi_arch}.efi"
+        fi
+    done
 
     if [ ! -f "${esp_path}/EFI/${vendor}/BOOT${efi_arch_upper}.CSV" ] && command -v iconv >/dev/null 2>&1; then
         printf "shim%s.efi,%s,\\\\EFI\\\\systemd\\\\systemd-boot%s.efi \\\\0,This is the boot entry for %s\n" "${efi_arch}" "${vendor_upper}" "${efi_arch}" "${vendor_upper}" | iconv -t UCS-2LE > "${esp_path}/EFI/${vendor}/BOOT${efi_arch_upper}.CSV"
-- 
2.49.0

From 8a003fb02862f7b9403e02b84e9567277030058c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Halimi?= <raphael.hal...@gmail.com>
Date: Sat, 31 May 2025 19:22:36 +0200
Subject: [PATCH 3/4] Drop make-machine-id-directory option

---
 debian/systemd-boot.postinst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/debian/systemd-boot.postinst b/debian/systemd-boot.postinst
index 86d1977b87..a932e01edb 100644
--- a/debian/systemd-boot.postinst
+++ b/debian/systemd-boot.postinst
@@ -138,7 +138,7 @@ if [ "$1" = configure ] && bootctl --print-esp-path > /dev/null 2>&1; then
     if bootctl is-installed > /dev/null 2>&1; then
         bootctl update --graceful
     else
-        bootctl install --make-machine-id-directory=auto
+        bootctl install
     fi
     install_shim install
 
-- 
2.49.0

From 1ec6dd86038d13651de1c893d08f215248ef7eee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Halimi?= <raphael.hal...@gmail.com>
Date: Sat, 31 May 2025 19:24:28 +0200
Subject: [PATCH 3/4] Fix loader.conf

---
 debian/systemd-boot.postinst | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/debian/systemd-boot.postinst b/debian/systemd-boot.postinst
index 86d1977b87..25e177f421 100644
--- a/debian/systemd-boot.postinst
+++ b/debian/systemd-boot.postinst
@@ -33,13 +33,30 @@ esac
 vendor="${ID:-debian}"
 vendor_upper="$(echo "$vendor" | cut -c1 | tr '[:lower:]' '[:upper:]')$(echo "$vendor" | cut -c2-)"
 
+esp_path="$(bootctl --quiet --print-esp-path 2>/dev/null)"
+
 get_boot_entry () {
     # 730079007300740065006d0064002d0062006f006f007400 is 'systemd-boot' encoded in UTF-16-LE"
     efibootmgr | grep -i -q "Boot.*${vendor_upper}.*EFI\\\\${vendor}\\\\shim${efi_arch}.efi.*systemd-boot${efi_arch}.efi\|Boot.*${vendor_upper}.*EFI\\\\${vendor}\\\\shim${efi_arch}.efi.*730079007300740065006d0064002d0062006f006f007400" | cut -d' ' -f1 | sed -e 's/Boot//' -e 's/*//'
 }
 
+fix_loader_conf () {
+    if [ -z "${esp_path}" ]; then
+        return
+    fi
+    if [ -f "/etc/machine-id" ] ; then
+        machine_id="$(cat "/etc/machine-id")"
+        loader_conf="${esp_path}/loader/loader.conf"
+        if [ -f "$loader_conf" ] ; then
+            if grep -q "^default" "$loader_conf" && ! grep -q "^default $machine_id" "$loader_conf" ; then
+                sed -i 's/^default /#&/' "$loader_conf"
+                printf "default %s-*\n" "$machine_id" >> "$loader_conf"
+            fi
+        fi
+    fi
+}
+
 remove_shim() {
-    esp_path="$(bootctl --quiet --print-esp-path 2>/dev/null)"
     if [ -z "$esp_path" ]; then
         return
     fi
@@ -81,7 +98,6 @@ install_shim() {
         return
     fi
 
-    esp_path="$(bootctl --quiet --print-esp-path 2>/dev/null)"
     if [ -z "$esp_path" ]; then
         return
     fi
@@ -139,6 +155,7 @@ if [ "$1" = configure ] && bootctl --print-esp-path > /dev/null 2>&1; then
         bootctl update --graceful
     else
         bootctl install --make-machine-id-directory=auto
+        fix_loader_conf
     fi
     install_shim install
 
-- 
2.49.0

From 19b3022f2641932c96aef52f14156b2b894aa92c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Halimi?= <raphael.hal...@gmail.com>
Date: Sat, 31 May 2025 19:37:13 +0200
Subject: [PATCH 4/4] Cleanup shim helpers on remove

---
 debian/systemd-boot.postinst | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/debian/systemd-boot.postinst b/debian/systemd-boot.postinst
index 25e177f421..ff9ed72921 100644
--- a/debian/systemd-boot.postinst
+++ b/debian/systemd-boot.postinst
@@ -73,6 +73,12 @@ remove_shim() {
         rm -f "${esp_path}/EFI/BOOT/BOOT${efi_arch_upper}.efi"
     fi
 
+    for f in fb mm; do
+        if [ -f "${esp_path}/EFI/BOOT/${f}${efi_arch}.efi" ] && [ "$(<"${esp_path}/EFI/BOOT/${f}${efi_arch}.efi" sha256sum)" = "$(<"${esp_path}/EFI/${vendor}/${f}${efi_arch}.efi" sha256sum)" ]; then
+            rm -f "${esp_path}/EFI/BOOT/${f}${efi_arch}.efi"
+        fi
+    done
+
     for f in shim fb mm; do
         rm -f "${esp_path}/EFI/${vendor}/${f}${efi_arch}.efi"
     done
-- 
2.49.0

Reply via email to