]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
mkosi-obs: sign bootloaders in ESPs
authorLuca Boccassi <luca.boccassi@gmail.com>
Tue, 25 Feb 2025 00:11:46 +0000 (00:11 +0000)
committerLuca Boccassi <luca.boccassi@gmail.com>
Tue, 25 Feb 2025 00:12:42 +0000 (00:12 +0000)
If there are bootloaders in the ESP(s), sign them too. Useful to
ensure everything is signed also with the certificate in the project
where the image is built.

mkosi/resources/mkosi-obs/mkosi.build
mkosi/resources/mkosi-obs/mkosi.postoutput

index 605c41f14218ceae11e6ee7e8cf230b7a2021bfa..358d205cb52e877f18976155f2fc04f224274992 100755 (executable)
@@ -85,7 +85,52 @@ while read -r SIG; do
         fi
     fi
 done < <(find hashes/ukis hashes/kernels -type f \( -name '*efi.sig' -o -name 'vmlinu*.sig' \) -printf '%P\n')
-rm -rf nss-db "$OUTPUTDIR"/*.sig hashes/ukis
+rm -rf "$OUTPUTDIR"/*.sig hashes/ukis
+
+# If there are signed bootloaders, install them in the ESP
+while read -r BOOTLOADER; do
+    unsigned="$(basename "${BOOTLOADER%.sig}")"
+    signed="$(basename "${BOOTLOADER%.sig}".signed)"
+    ddi="${OUTPUTDIR}/${BOOTLOADER%%/*}"
+    # remove leading directory from BOOTLOADER
+    dest="${BOOTLOADER#*/}"
+    dest="${dest%.sig}"
+
+    if [ -f "${ddi}.zst" ]; then
+        unzstd "${ddi}.zst"
+    fi
+
+    offset="$(systemd-repart --json=short "$ddi" | jq -r '.[] | select(.type == "esp") | .offset')"
+    if [ -z "$offset" ] || [ "$offset" = "null" ]; then
+        if [ -f "$ddi.zst" ]; then
+            rm -f "$ddi"
+        fi
+        continue
+    fi
+
+    rm -f "$unsigned"
+    mcopy -i "${ddi}@@${offset}" "::$dest" "$unsigned"
+
+    # ensure the EFI hash matches before and after attaching the signature
+    old_hash=$(pesign -n sql:"$nss_db" -h -P -i "$unsigned" | cut -d' ' -f1)
+
+    pesign -n sql:"$nss_db" --force -c cert -i "$unsigned" -o "$signed" -d sha256 -I "hashes/bootloaders/${BOOTLOADER%.sig}" -R "hashes/bootloaders/${BOOTLOADER}"
+
+    new_hash=$(pesign -n sql:"$nss_db" -h -i "$signed" | cut -d' ' -f1)
+    if [ "$old_hash" != "$new_hash" ]; then
+        echo "Pesign hash mismatch error: $old_hash $new_hash"
+        exit 1
+    fi
+
+    mcopy -o -i "${ddi}@@${offset}" "$signed" "::$dest"
+
+    if [ -f "${ddi}.zst" ]; then
+        zstd --force "$ddi"
+        rm -f "$ddi"
+    fi
+done < <(find "hashes/bootloaders/$(basename "$ddi")/" -type f -iname '*.efi.sig' -printf '%P\n')
+rm -rf hashes/bootloaders
+rm -rf nss-db
 
 # Second step: if there are PCR policy signatures, rebuild the JSON
 # blobs with the attached signatures
index 710e1ebb0f2344e613c13cd7342463be48775e4f..19569aa5342bc3278e7a51324b15c9450351f3f8 100755 (executable)
@@ -15,8 +15,10 @@ declare -a KERNELS
 mapfile -t KERNELS < <(find "$OUTPUTDIR" -type f -name "vmlinu*" -printf '%P\n')
 declare -a ROOTHASHES
 mapfile -t ROOTHASHES < <(find "$OUTPUTDIR" -type f -name "*.roothash" -printf '%P\n')
+declare -a DDIS
+mapfile -t DDIS < <(find "$OUTPUTDIR" -type f -name "*.raw*")
 
-if ((${#UKIS[@]} == 0)) && ((${#KERNELS[@]} == 0)) && ((${#ROOTHASHES[@]} == 0)); then
+if ((${#UKIS[@]} == 0)) && ((${#KERNELS[@]} == 0)) && ((${#ROOTHASHES[@]} == 0)) && ((${#DDIS[@]} == 0)); then
     echo "No unsigned files found, exiting"
     exit 0
 fi
@@ -59,6 +61,38 @@ for f in "${ROOTHASHES[@]}"; do
     fi
 done
 
+# Handle bootloaders separately from UKIs
+for ddi in "${DDIS[@]}"; do
+    test -f "$ddi" || continue
+    if [[ $ddi == *.zst ]]; then
+        unzstd "${ddi}"
+    fi
+    offset="$(systemd-repart --json=short "${ddi%.zst}" | jq -r '.[] | select(.type == "esp") | .offset')"
+    if [ "$offset" = "null" ]; then
+        if [[ $ddi == *.zst ]]; then
+            rm -f "${ddi%.zst}"
+        fi
+        continue
+    fi
+
+    rm -rf EFI
+    mcopy -s -i "${ddi%.zst}@@${offset}" ::EFI EFI || true
+    find EFI
+
+    # UKIs are handled separately
+    rm -rf EFI/Linux
+
+    while read -r BOOTLOADER; do
+        mkdir -p "hashes/bootloaders/$(basename "${ddi%.zst}")/$(dirname "$BOOTLOADER")"
+        pesign --force -n sql:"$nss_db" -i "$BOOTLOADER" -E "hashes/bootloaders/$(basename "${ddi%.zst}")/$BOOTLOADER"
+    done < <(find EFI -type f -iname '*.efi')
+
+    if [[ $ddi == *.zst ]]; then
+        rm -f "${ddi%.zst}"
+    fi
+    rm -rf EFI
+done
+
 # Pack everything into a CPIO archive and place it where OBS expects it
 pushd hashes
 find . -type f | cpio -H newc -o >"$OUTPUTDIR/hashes.cpio.rsasign"