Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package sdbootutil for openSUSE:Factory checked in at 2026-03-12 22:20:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/sdbootutil (Old) and /work/SRC/openSUSE:Factory/.sdbootutil.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "sdbootutil" Thu Mar 12 22:20:45 2026 rev:94 rq:1338358 version:1+git20260311.73a155b Changes: -------- --- /work/SRC/openSUSE:Factory/sdbootutil/sdbootutil.changes 2026-03-04 20:59:41.306728375 +0100 +++ /work/SRC/openSUSE:Factory/.sdbootutil.new.8177/sdbootutil.changes 2026-03-12 22:22:19.066385748 +0100 @@ -1,0 +2,12 @@ +Wed Mar 11 14:56:51 UTC 2026 - Alberto Planas Dominguez <[email protected]> + +- Update to version 1+git20260311.73a155b: + * Add ESP_FREE_SPACE config option + * Merge both 710 components for grub2-bls + * Allow partial unenrollment + * Drop pcr-oracle + * Edit crypttab options only for tracked devices + * Fix bootloader PCR measurements for portable installations + * Fix PCR predictions for grub2-bls without shim + +------------------------------------------------------------------- Old: ---- sdbootutil-1+git20260303.90d816d.obscpio New: ---- sdbootutil-1+git20260311.73a155b.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ sdbootutil.spec ++++++ --- /var/tmp/diff_new_pack.87sutP/_old 2026-03-12 22:22:19.754414212 +0100 +++ /var/tmp/diff_new_pack.87sutP/_new 2026-03-12 22:22:19.758414377 +0100 @@ -18,7 +18,7 @@ %global rustflags '-Clink-arg=-Wl,-z,relro,-z,now' Name: sdbootutil -Version: 1+git20260303.90d816d +Version: 1+git20260311.73a155b Release: 0 Summary: Bootctl wrapper for BLS boot loaders License: MIT @@ -36,7 +36,6 @@ Requires: jq Requires: keyutils Requires: openssl -Requires: pcr-oracle Requires: qrencode Requires: sed Requires: (%{name}-snapper if (snapper and btrfsprogs)) ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.87sutP/_old 2026-03-12 22:22:19.810416529 +0100 +++ /var/tmp/diff_new_pack.87sutP/_new 2026-03-12 22:22:19.810416529 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/openSUSE/sdbootutil.git</param> - <param name="changesrevision">90d816d884019f7ffa0dc7b8e1dc866bfe7f922a</param></service></servicedata> + <param name="changesrevision">73a155bdc01e5e2e29de7d2e30f80e63eb713acd</param></service></servicedata> (No newline at EOF) ++++++ sdbootutil-1+git20260303.90d816d.obscpio -> sdbootutil-1+git20260311.73a155b.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdbootutil-1+git20260303.90d816d/sdbootutil new/sdbootutil-1+git20260311.73a155b/sdbootutil --- old/sdbootutil-1+git20260303.90d816d/sdbootutil 2026-03-03 11:19:37.000000000 +0100 +++ new/sdbootutil-1+git20260311.73a155b/sdbootutil 2026-03-11 15:54:50.000000000 +0100 @@ -39,12 +39,12 @@ arg_default_snapshot= arg_ask_key_pin_or_pw= arg_method= -arg_signed_policy= arg_no_measure_pcr= arg_measure_pcr= arg_pcr= arg_rootfs= arg_rootfs_data= +arg_esp_free_space= arg_force= have_snapshots= # for x in vmlinuz image vmlinux linux bzImage uImage Image zImage; do @@ -133,7 +133,6 @@ when initial enrollment --ask-pw Ask password when initial enrollment --method "tpm2", "tpm2+pin", "fido2", "password", "recovery-key" - --signed-policy Use signed policy for TPM2 enrollment --no-measure-pcr During enrollment, do not include PCR 15 protection --measure-pcr Force update of PCR 15 prediction for LUKS2 volume key (requires LUKS2 password to accessing the volume key) @@ -144,6 +143,7 @@ valid selectors (uuid, partuuid, label, partlabel, device) --rootfs-data Extra data when --rootfs is used + --esp-free-space Percentage of free space in the ESP --force Force certain operations inside a transaction -v, --verbose More verbose output --start-trace-code Create /var/log/sdbootutil.log to trace the code. @@ -348,6 +348,8 @@ ROOTFS="uuid" fi ROOTFS="${arg_rootfs:-$ROOTFS}" + + ESP_FREE_SPACE="${arg_esp_free_space:-10}" } create_default_config_file() @@ -364,6 +366,9 @@ # Parameter used in the cmdline to find the root (device, [part]uuid, [part]label) # For encrypted devices it is forced "device" ROOTFS="$ROOTFS" + + # Percentage (%) of free space in the ESP that sdbootutil should guarantee + ESP_FREE_SPACE="$ESP_FREE_SPACE" EOF } @@ -397,6 +402,7 @@ # If arguments are passed, use them FDE_SEAL_PCR_LIST="${arg_pcr:-$FDE_SEAL_PCR_LIST}" ROOTFS="${arg_rootfs:-$ROOTFS}" + ESP_FREE_SPACE="${arg_esp_free_space:-$ESP_FREE_SPACE}" } is_secure_boot() @@ -1012,9 +1018,14 @@ echo $((size / 1024 + 1)) } +boot_space() +{ + echo $(($(findmnt --noheadings --bytes -o SIZE --target "$boot_root" | head -n 1) / 1024)) +} + boot_free_space() { - echo $(($(findmnt -n -b -o AVAIL --target "$boot_root" | head -n 1) / 1024)) + echo $(($(findmnt --noheadings --bytes -o AVAIL --target "$boot_root" | head -n 1) / 1024)) } regex_snapshot_ids_for_free_space() @@ -1117,9 +1128,15 @@ local snapshot="$1" local required_size="$2" - info "Required free space in ESP: $required_size KB" + local required_size_extra + required_size_extra=$(("$(boot_space)" * $ESP_FREE_SPACE / 100)) + + info "Required free space in ESP: ${required_size}KB + ${required_size_extra}KB (${ESP_FREE_SPACE}% ESP size)" + + required_size=$(($required_size + $required_size_extra)) # If there is already free space, shortcut the code + local free_space free_space="$(boot_free_space)" [ "$required_size" -gt "$free_space" ] || return 0 @@ -1399,6 +1416,9 @@ dbg "Calling bootctl cleanup" bootctl -q cleanup 2> /dev/null + + # Guarantee ESP_FREE_SPACE + make_free_space "${1:-$root_snapshot}" "0" } list_entries() @@ -1688,6 +1708,8 @@ if [ -z "$1" ]; then if [ -e "$shimdir/shim.efi" ]; then fn="$boot_root$boot_dst/grub.efi" + elif [ -n "$arg_portable" ]; then + fn="$boot_root/EFI/BOOT/BOOT${firmware_arch^^}.EFI" else local bootloader bootloader="$(find_bootloader)" @@ -2479,27 +2501,10 @@ # \x00, as is UTF-16 enconding), but this is missing in # GRUB2-BLS <= 2.12 echo -n "$cmdline" > "$tmpdir/cmdline" - if is_sdboot; then - pcrlock \ - lock-kernel-cmdline \ - --pcrlock="$tmpdir/cmdline.pcrlock" \ - "$tmpdir/cmdline" - else - if systemd-analyze compare-versions "$(bootloader_version)" "<" "2.13"; then - iconv -t UTF-16LE -o "$tmpdir/cmdline.utf16" "$tmpdir/cmdline" - pcrlock \ - lock-raw \ - --pcr=9 \ - --pcrlock="$tmpdir/cmdline.pcrlock" \ - "$tmpdir/cmdline.utf16" - rm "$tmpdir/cmdline.utf16" - else - pcrlock \ - lock-kernel-cmdline \ - --pcrlock="$tmpdir/cmdline.pcrlock" \ - "$tmpdir/cmdline" - fi - fi + pcrlock \ + lock-kernel-cmdline \ + --pcrlock="$tmpdir/cmdline.pcrlock" \ + "$tmpdir/cmdline" pcrlock \ lock-kernel-initrd \ --pcrlock="$tmpdir/initrd.pcrlock" \ @@ -2515,26 +2520,26 @@ # 710-kernel-cmdline-boot-loader.pcrlock.d is not part of the # pcrlock standards - if is_sdboot; then - # We cannot use lock-kernel-cmdline, as it ignore - # --pcr=12, and assign PCR 9 in any case - echo -ne "$cmdline\x00" > "$tmpdir/cmdline" - iconv -t UTF-16LE -o "$tmpdir/cmdline.utf16" "$tmpdir/cmdline" - pcrlock \ - lock-raw \ - --pcr=12 \ - --pcrlock="/var/lib/pcrlock.d/710-kernel-cmdline-boot-loader.pcrlock.d/cmdline-$suffix.pcrlock" \ - "$tmpdir/cmdline.utf16" - rm "$tmpdir/cmdline.utf16" - rm "$tmpdir/cmdline" - fi + + # We cannot use lock-kernel-cmdline, as it ignore --pcr=12, + # and assign PCR 9 in any case + echo -ne "$cmdline\x00" > "$tmpdir/cmdline" + iconv -t UTF-16LE -o "$tmpdir/cmdline.utf16" "$tmpdir/cmdline" + pcrlock \ + lock-raw \ + --pcr=12 \ + --pcrlock="/var/lib/pcrlock.d/710-kernel-cmdline-boot-loader.pcrlock.d/cmdline-$suffix.pcrlock" \ + "$tmpdir/cmdline.utf16" + rm "$tmpdir/cmdline.utf16" + rm "$tmpdir/cmdline" } -pcrlock_grub2_bls_kernel_initrd() +pcrlock_grub2_bls_kernel_initrd_cmdline_initrd() { local linux="$1" local initrd="$2" - local suffix="$3" + local cmdline="$3" + local suffix="$4" local elements=("$linux" "$initrd") local locks=() @@ -2548,10 +2553,41 @@ "$element" 2> /dev/null locks+=("$tmpdir/element-$n.pcrlock") done - mkdir -p /var/lib/pcrlock.d/710-grub2-bls-kernel-initrd-entry.pcrlock.d + + # When using systemd-boot or GRUB2-BLS >= 2.14, the cmdline is + # send to the kernel via UEFI with null termination (double + # \x00, as is UTF-16 enconding), but this is missing in + # GRUB2-BLS <= 2.12 + echo -n "$cmdline" > "$tmpdir/cmdline" + n=$((n+1)) + if systemd-analyze compare-versions "$(bootloader_version)" "<" "2.13"; then + iconv -t UTF-16LE -o "$tmpdir/cmdline.utf16" "$tmpdir/cmdline" + pcrlock \ + lock-raw \ + --pcr=9 \ + --pcrlock="$tmpdir/element-$n.pcrlock" \ + "$tmpdir/cmdline.utf16" + rm "$tmpdir/cmdline.utf16" + else + pcrlock \ + lock-kernel-cmdline \ + --pcrlock="$tmpdir/element-$n.pcrlock" \ + "$tmpdir/cmdline" + fi + locks+=("$tmpdir/element-$n.pcrlock") + rm "$tmpdir/cmdline" + + n=$((n+1)) + pcrlock \ + lock-kernel-initrd \ + --pcrlock="$tmpdir/element-$n.pcrlock" \ + "$initrd" 2> /dev/null + locks+=("$tmpdir/element-$n.pcrlock") + + mkdir -p /var/lib/pcrlock.d/710-grub2-bls-kernel-initrd-kernel-cmdline-initrd-entry.pcrlock.d jq --slurp '{"records": [.[].records[0]]}' \ "${locks[@]}" \ - > "/var/lib/pcrlock.d/710-grub2-bls-kernel-initrd-entry.pcrlock.d/kernel-initrd-$suffix.pcrlock" + > "/var/lib/pcrlock.d/710-grub2-bls-kernel-initrd-kernel-cmdline-initrd-entry.pcrlock.d/kernel-initrd-cmdline-initrd-$suffix.pcrlock" rm "${locks[@]}" } @@ -2802,32 +2838,41 @@ # initrd gets updated, a new entry with different hashes # will appear here # - # If not kernel/initrd gets updated, the possible valies for - # PCR#9 are 2x2xn, but if a kernel gets update is 2x2xnx2 + # If not kernel/initrd gets updated, the possible values for + # PCR#9 are 2 for 641 (the shift and the next one that + # includes the new grubenv), 2 for 643 (again, the shift and + # the variation that includes the hash of the new entry), n + # for 710-kernel (one per snapshot), and 2 for 710-grub2 (only + # if there is a new kernel or initrd): [2x]2xn[x2] # # Solutions: # - GRUB2-BLS use BLI and drops `grubenv` - # - PolicyOR limit is resolved in systemd-pcrlock # - MicroOS uses only systemd-boot - # - Make n=2 and predict only one kernel. + # - PolicyOR limit is resolved in systemd-pcrlock + # - Merge both 710 components, to decrease the variations + # - Make n=2 (710-kernel-cmdline-initrd-entry) # - # This code select the last option. One issue is that if a - # kernel is updated and a rollback is needed, the password - # will be asked. To resolve this we can drop PCR#9 when there - # is a new kernel and allow the rollback without a password, - # and lock on PCR#9 during boot time, via the - # sdbootutil-update-predictions service. + # For GRUB2 2.14 we drop grubenv with the BLI patches. # - # As systemd-pcrlock does not drop a PCR that exceed the - # PolicyPR limit, we need to first create a prediction and - # count the values - local limit=1 - is_transactional || limit=3 + # The merge solution makes sense. The order would be: + # - GRUB2: full sha256 of the kernel + # - GRUB2: sha256 of the initrd + # - Kernel: sha256 of the cmdline + # - Kernel: sha256 of the initrd (rep) + # + # The kernel is again measured a-la pesign in PCR#4 via + # 650-kernel-efi-application (by UEFI/shim) - # Join the kernel and the initrd in a single component - shift_component 710-grub2-bls-kernel-initrd-entry + # Remove old components before the merge + rm -fr /var/lib/pcrlock.d/710-grub2-bls-kernel-initrd-entry.pcrlock.d + rm -fr /var/lib/pcrlock.d/710-kernel-cmdline-initrd-entry.pcrlock.d + + # Join the kernel, initrd (GRUB2) and cmdline, initrd (kernel) + # in a single component + shift_component 710-grub2-bls-kernel-initrd-kernel-cmdline-initrd-entry n=0 - while read -r linux; do + while read -r cmdline; do + read -r linux read -r initrd [ -f "${boot_root}$linux" ] || { info "Missing ${boot_root}$linux, ignoring entry for prediction" @@ -2838,54 +2883,24 @@ continue } n=$((n+1)) - [ "$n" -le "$limit" ] || { - info "More than $limit variations for 710-grub2-bls-kernel-initrd-entry" - continue - } - pcrlock_grub2_bls_kernel_initrd "${boot_root}$linux" "${boot_root}$initrd" "$n" - done < <(jq --raw-output 'sort_by(.priority, (.kernel | map(-.))) | .[] | .linux, .initrd[0]' "$entryfile") - - # Generate variation for 710-grub2-bls-kernel-initrd-entry for the - # same reason than before. - n=0 - if [ "$SDB_ADD_INITIAL_COMPONENT" = "1" ]; then - while read -r linux; do - read -r initrd - n=$((n+1)) - pcrlock_grub2_bls_kernel_initrd "${tmpdir}$linux" "${tmpdir}$initrd" "0-$n" - done < <(jq --raw-output '.[] | .linux, .initrd[0]' "$initialentryfile") - fi - - local limit=2 - is_transactional || limit=3 - - # Join the cmdline and the initrd in a single component - # TODO: If shim is not installed, should we drop this? - shift_component 710-kernel-cmdline-initrd-entry - n=0 - while read -r cmdline; do - read -r linux - read -r initrd - [ -f "${boot_root}$linux" ] || continue - [ -f "${boot_root}$initrd" ] || continue - n=$((n+1)) - [ "$n" -le "$limit" ] || { - info "More than $limit variations for 710-kernel-cmdline-initrd-entry" + [ "$n" -le 4 ] || { + info "More than 4 variations for 710-grub2-bls-kernel-initrd-entry" continue } - pcrlock_cmdline_initrd "BOOT_IMAGE=${grub2_bls_drive}$linux $cmdline" "${boot_root}$initrd" "$n" - done < <(jq --raw-output 'sort_by(.priority, (.kernel | map(-.))) | .[] | (.options, .linux, .initrd[0])' "$entryfile") + pcrlock_grub2_bls_kernel_initrd_cmdline_initrd "${boot_root}$linux" "${boot_root}$initrd" "BOOT_IMAGE=${grub2_bls_drive}$linux $cmdline" "$n" + done < <(jq --raw-output 'sort_by(.priority, (.kernel | map(-.))) | .[] | .options, .linux, .initrd[0]' "$entryfile") - # Generate variation for 710-kernel-cmdline-initrd-entry for - # the same reason than before + # Generate variation for + # 710-grub2-bls-kernel-initrd-kernel-cmdline-initrd-entry for + # the same reason than before. n=0 if [ "$SDB_ADD_INITIAL_COMPONENT" = "1" ]; then while read -r cmdline; do read -r linux read -r initrd n=$((n+1)) - pcrlock_cmdline_initrd "BOOT_IMAGE=${grub2_bls_drive}$linux $cmdline" "${tmpdir}$initrd" "0-$n" - done < <(jq --raw-output '.[] | (.options, .linux, .initrd[0])' "$initialentryfile") + pcrlock_grub2_bls_kernel_initrd_cmdline_initrd "${tmpdir}$linux" "${tmpdir}$initrd" "BOOT_IMAGE=${grub2_bls_drive}$linux $cmdline" "0-$n" + done < <(jq --raw-output '.[] | .options, .linux, .initrd[0]' "$initialentryfile") fi } @@ -2976,7 +2991,7 @@ { local pcrs="$FDE_SEAL_PCR_LIST" - info "Generating TPM2 predictions with systemd-pcrlock" + info "Generating TPM2 predictions" # Select the affected entries select_entries_for_prediction @@ -3024,10 +3039,10 @@ if is_sdboot; then bootloader_filename="systemd-boot${firmware_arch,,}.efi" elif is_grub2_bls; then - bootloader_filename="grub.efi" + bootloader_filename="grubbls.efi" fi if [ -n "$arg_portable" ]; then - bootloader_path="${boot_root}/EFI/BOOT/${bootloader_filename}" + bootloader_path="${boot_root}/EFI/BOOT/BOOT${firmware_arch^^}.EFI" else bootloader_path="${boot_root}${boot_dst}/${bootloader_filename}" fi @@ -3133,66 +3148,6 @@ } } -get_pcrs() -{ - local pcrs - local jq_pcr='.tokens[]|select(.type == "systemd-tpm2")|."tpm2_pubkey_pcrs"|join(",")' - # We can have multiple devices, each one of them with - # different PCRs - while read -r dev; do - pcrs=$(cryptsetup luksDump --dump-json-metadata "$dev" | jq -r "$jq_pcr") - [ -z "$pcrs" ] || echo "$pcrs" - done <<<"$(blkid -t TYPE=crypto_LUKS -o device)" -} - -generate_tpm2_predictions_pcr_oracle() -{ - local entry - local all_pcrs - - info "Generating TPM2 predictions with pcr-oracle" - - # Select the affected entries - select_entries_for_prediction - - all_pcrs=$(get_pcrs) - [ -n "$all_pcrs" ] || all_pcrs="$FDE_SEAL_PCR_LIST" - - rm -f /etc/systemd/tpm2-pcr-signature.json - - # We make as many predictions as |all_pcrs| * |entries| to - # cover all the combinations. pcr-oracle is smart to include - # the entry only one time, so we will not have duplications. - # This is a step for multi device configurations. - local -a entries - mapfile -t entries < <(jq -r '.[]|.id' "$entryfile") - if [ -z "${entries[0]}" ]; then - err "No bootloader entries found" - fi - for pcrs in $all_pcrs; do - for entry in "${entries[@]}"; do - info "Generate prediction for $entry with PCRs $pcrs" - if ! pcr-oracle \ - --private-key /etc/systemd/tpm2-pcr-private-key.pem \ - --from eventlog \ - --output /etc/systemd/tpm2-pcr-signature.json \ - --target-platform=systemd \ - --boot-entry "${entry}" \ - sign "$pcrs"; then - err "Failed to install TPM predictions for ${entry}" - fi - done - done - - # Publish the assets in the ESP, so can be imported by - # dracut-pcr-signature - cp /etc/systemd/tpm2-pcr-public-key.pem "${boot_root}${boot_dst}" - [ -e /etc/systemd/tpm2-pcr-signature.json ] && \ - cp /etc/systemd/tpm2-pcr-signature.json "${boot_root}${boot_dst}" && { - echo "Signed policy created" - } -} - get_device_password() { local dev="$1" @@ -3380,23 +3335,12 @@ return 0 } - # The PCR list is used by both models (pcr-oracle, - # systemd-pcrlock). The first one will try first to get the - # PCRs from the LUKS2 header, that will succeed in the normal - # situation but fail during the initial enrollment. This is - # because we generate the prediction first and later we do the - # enrollment - [ -n "$FDE_SEAL_PCR_LIST" ] || { - warn "FDE_SEAL_PCR_LIST no found in /etc/default/sdbootutil (manual enrollment?)" - warn "No TPM2 predictions generated" + ! is_pcr_oracle || { + warn "System enrolled with pcr-oracle. Re-enroll to use systemd-pcrlock" return 0 } - if is_pcr_oracle; then - generate_tpm2_predictions_pcr_oracle - elif have_pcrlock; then - generate_tpm2_predictions_pcrlock || return 1 - fi + generate_tpm2_predictions_pcrlock # Generate a PCR 15 prediction only in certain cases, as for # now this will ask the password (can be resolved by an @@ -3478,16 +3422,31 @@ tpm2_getcap properties-variable | grep -q 'inLockout: *1' } +is_same_device() +{ + local dev1="$1" + local dev2="$2" + + # If the device name is UUID=, convert as a real device name + [[ "$dev1" = "UUID="* ]] && dev1="$(blkid --uuid "${dev1#"UUID="}")" + [[ "$dev2" = "UUID="* ]] && dev2="$(blkid --uuid "${dev2#"UUID="}")" + + dev1="$(readlink -f "$dev1")" + dev2="$(readlink -f "$dev2")" + + local id1 id2 + id1="$(stat -c "%t:%T" "$dev1")" + id2="$(stat -c "%t:%T" "$dev2")" + + [ "$id1" = "$id2" ] +} + add_crypttab_option() { - # This version will share the same options for all crypto_LUKS - # devices. This imply that all of them will be unlocked by - # the same TPM2, or the same FIDO2 key. - # - # The ones ignored will be untouched. - local option="$1" + local dev="$1" + local option="$2" - dbg "Adding \"$option\" to /etc/crypttab" + dbg "Adding \"$option\" for \"$dev\" to /etc/crypttab" dbg_cat "/etc/crypttab" local crypttab @@ -3501,7 +3460,7 @@ local opts while read -r name device key opts; do [[ "$name" = \#* ]] && continue - if [[ "$opts" != *"x-sdbootutil.ignore"* ]] && [[ "$opts" != *"$option"* ]]; then + if is_same_device "$dev" "$device" && [[ "$opts" != *"x-sdbootutil.ignore"* ]] && [[ "$opts" != *"$option"* ]]; then [ -z "$opts" ] && opts="$option" || opts="$opts,$option" # crypttab has changed so initrd needs to be # updated @@ -3513,19 +3472,16 @@ mv -Z "$crypttab" /etc/crypttab chmod 644 /etc/crypttab - dbg "Added \"$option\" to /etc/crypttab" + dbg "Added \"$option\" for \"$dev\" to /etc/crypttab" dbg_cat "/etc/crypttab" } remove_crypttab_option() { - # Similar to add_crypttab_option, the option will be removed - # from all the entries. - # - # The ones ignored will be untouched. - local option="$1" + local dev="$1" + local option="$2" - dbg "Removing \"$option\" to /etc/crypttab" + dbg "Removing \"$option\" for \"$dev\" to /etc/crypttab" dbg_cat "/etc/crypttab" local crypttab @@ -3539,7 +3495,7 @@ local opts while read -r name device key opts; do [[ "$name" = \#* ]] && continue - if [[ "$opts" != *"x-sdbootutil.ignore"* ]] && [[ "$opts" = *"$option"* ]]; then + if is_same_device "$dev" "$device" && [[ "$opts" != *"x-sdbootutil.ignore"* ]] && [[ "$opts" = *"$option"* ]]; then opts="${opts#"$option",}" opts="${opts//,"$option"}" opts="${opts//"$option"}" @@ -3554,19 +3510,10 @@ mv -Z "$crypttab" /etc/crypttab chmod 644 /etc/crypttab - dbg "Removed \"$option\" to /etc/crypttab" + dbg "Removed \"$option\" for \"$dev\" to /etc/crypttab" dbg_cat "/etc/crypttab" } -generate_rsa_key() -{ - pcr-oracle \ - --rsa-generate-key \ - --private-key /etc/systemd/tpm2-pcr-private-key.pem \ - --public-key /etc/systemd/tpm2-pcr-public-key.pem \ - store-public-key -} - set_unlock_method() { local dev="$1" @@ -3590,16 +3537,16 @@ fi } -enroll_pcrlock() +enroll_tpm2() { local dev="$1" local tpm2_pin="$2" local extra_args=() if [ -z "$tpm2_pin" ]; then - info "Enrolling with TPM2 (pcrlock): $dev" + info "Enrolling with TPM2: $dev" else - info "Enrolling with TPM2+PIN (pcrlock): $dev" + info "Enrolling with TPM2+PIN: $dev" extra_args+=(--tpm2-with-pin=1) fi @@ -3637,45 +3584,6 @@ systemctl --quiet enable sdbootutil-update-predictions.service || true } -enroll_pcroracle() -{ - local dev="$1" - local tpm_pin="$2" - local extra_args=() - - if [ -z "$tpm_pin" ]; then - info "Enrolling with TPM2 (pcr-oracle): $dev" - else - info "Enrolling with TPM2+PIN (pcr-oracle): $dev" - extra_args+=(--tpm2-with-pin=1) - fi - - if [ ! -f /etc/systemd/tpm2-pcr-signature.json ]; then - warn "Could not find /etc/systemd/tpm2-pcr-signature.json" - fi - - set_unlock_method "$dev" - if [ -n "$unlock_method" ]; then - extra_args+=("$unlock_method") - fi - - # Note that the PCRs are now not stored in the LUKS2 header - if NEWPIN="$tpm_pin" systemd-cryptenroll \ - --wipe-slot=tpm2 \ - --tpm2-device=auto \ - "${extra_args[@]}" \ - --tpm2-public-key=/etc/systemd/tpm2-pcr-public-key.pem \ - --tpm2-public-key-pcrs="${FDE_SEAL_PCR_LIST}" \ - "$dev"; then - # systemd-cryptenroll exits successfully even if the - # token was not enrolled. Manually check if the - # device has a tpm2 slot enrolled - systemd-cryptenroll "$dev" | grep -q "tpm2" - else - return 1 - fi -} - enroll_fido2() { local dev="$1" @@ -3805,23 +3713,7 @@ case "$arg_method" in "tpm2"|"tpm2+pin") - if ! is_pcr_oracle && have_pcrlock; then - enroll_pcrlock "$dev" "$pin_or_pw" || { - warn "Enrollment with systemd-pcrlock failed" - warn "Re-trying with pcr-oracle" - unenroll_pcrlock - # This function generates - # /etc/systemd/tpm2-pcr-public-key.pem - # which is needed by - # enroll_pcroracle - generate_rsa_key - enroll_pcroracle "$dev" "$pin_or_pw" - } - elif have_pcr_oracle; then - enroll_pcroracle "$dev" "$pin_or_pw" - else - info "No TMP2 enrollment mechanism found" - fi + enroll_tpm2 "$dev" "$pin_or_pw" ;; "fido2") @@ -3856,20 +3748,23 @@ info "Enrolling devices ($arg_method): ${tracked_devices[*]}" - have_pcrlock || have_pcr_oracle || err "Not systemd-pcrlock nor pcr-oracle found" - # Prepare /etc/crypttab and update initrd if required case "$arg_method" in "tpm2"|"tpm2+pin") - have_tpm2 || err "No TPM2 found found" + have_tpm2 || err "No TPM2 found" + have_pcrlock || err "No systemd-pcrlock found" ! in_lockout || err "The TPM2 is in lockout. Use 'tpm2_dictionarylockout -c [ -p passwd ]' to continue" - add_crypttab_option 'tpm2-device=auto' - [ -n "$arg_no_measure_pcr" ] || add_crypttab_option 'tpm2-measure-pcr=yes' + for dev in "${tracked_devices[@]}"; do + add_crypttab_option "$dev" 'tpm2-device=auto' + [ -n "$arg_no_measure_pcr" ] || add_crypttab_option "$dev" 'tpm2-measure-pcr=yes' + done ;; "fido2") have_fido2 || err "No FIDO2 key found" - add_crypttab_option 'fido2-device=auto' + for dev in "${tracked_devices[@]}"; do + add_crypttab_option "$dev" 'fido2-device=auto' + done ;; esac @@ -3889,42 +3784,16 @@ # generate a new initrd if [ "$arg_no_reuse_initrd" = "1" ]; then install_all_kernels "$root_snapshot" - # Avoid the call of generate_tpm2_predictions at the - # end of the script - update_predictions= - fi - - # If systemd-pcrlock is missing then we need to use - # pcr-oracle. For this we need to generate the RSA keys - # early. Also we need to set the PCRs used to assest the - # system status - if [ "$arg_method" = "tpm2" ] || [ "$arg_method" = "tpm2+pin" ]; then - if { ! have_pcrlock || [ -n "$arg_signed_policy" ]; } && have_pcr_oracle; then - # Once the RSA key is present, then - # is_pcr_oracle is true - generate_rsa_key - fi - - # During the initial enrollment it is expected that - # for systemd-pcrlock the recovery PIN will be - # extracted from the %u keyring "sdbootutil[-pin]" - # entry - generate_tpm2_predictions || { - # If the generation of prediction fails and we - # are trying with systemd-pcrlock, we try - # again with pcr-oracle - if ! is_pcr_oracle; then - echo "Predictions with systemd-pcrlock failed" - echo "Re-trying with pcr-oracle" - unenroll_pcrlock - # This function generates - # /etc/systemd/tpm2-pcr-public-key.pem - # which is needed by - # generate_tpm2_predictions - generate_rsa_key - generate_tpm2_predictions - fi - } + if [ "$arg_method" = "tpm2" ] || [ "$arg_method" = "tpm2+pin" ]; then + # During the initial enrollment it is expected + # that for systemd-pcrlock the recovery PIN + # will be extracted from the %u keyring + # "sdbootutil[-pin]" entry + generate_tpm2_predictions + # Avoid the call of generate_tpm2_predictions + # at the end of the script + update_predictions= + fi fi # For the PIN (tpm2+pin) or password (password), we can get it @@ -3956,7 +3825,7 @@ done } -unenroll_pcrlock() +unenroll_all_pcrlock() { systemctl --quiet disable sdbootutil-update-predictions.service || true pcrlock remove-policy &> /dev/null || true @@ -3972,7 +3841,7 @@ rm -f "${boot_root}${boot_dst}/measure-pcr-prediction.sha256" } -unenroll_pcr_oracle() +unenroll_all_pcr_oracle() { rm -f /etc/systemd/tpm2-pcr-private-key.pem rm -f /etc/systemd/tpm2-pcr-public-key.pem @@ -4052,13 +3921,17 @@ case "$arg_method" in "tpm2"|"tpm2+pin") have_tpm2 || err "No TPM2 found found" - remove_crypttab_option 'tpm2-device=auto' - remove_crypttab_option 'tpm2-measure-pcr=yes' + for dev in "${tracked_devices[@]}"; do + remove_crypttab_option "$dev" 'tpm2-device=auto' + remove_crypttab_option "$dev" 'tpm2-measure-pcr=yes' + done ;; "fido2") have_fido2 || err "No FIDO2 key found" - remove_crypttab_option 'fido2-device=auto' + for dev in "${tracked_devices[@]}"; do + remove_crypttab_option "$dev" 'fido2-device=auto' + done ;; esac if [ "$arg_no_reuse_initrd" = "1" ]; then @@ -4068,14 +3941,29 @@ update_predictions= fi - if [ "$arg_method" = "tpm2" ] || [ "$arg_method" = "tpm2+pin" ]; then - unenroll_pcrlock - unenroll_pcr_oracle - fi - for dev in "${tracked_devices[@]}"; do unenroll_device "$dev" done + + # We can have a partial unenroll, so we drop the policy and + # the assets only if there is no device left with a "tpm2" + # slot. Multiple devices shares the same NVIndex. + if [ "$arg_method" = "tpm2" ] || [ "$arg_method" = "tpm2+pin" ]; then + local unenroll_all=1 + # Reset the detected devices + tracked_devices=() + detect_tracked_devices + for dev in "${tracked_devices[@]}"; do + if have_slot "$dev" "tpm2"; then + unenroll_all=0 + break + fi + done + if [ "$unenroll_all" = 1 ]; then + unenroll_all_pcrlock + unenroll_all_pcr_oracle + fi + fi } eval_bootctl() @@ -4173,13 +4061,13 @@ [ask-pin]="" [ask-pw]="" [method]="_method" - [signed-policy]="" [no-measure-pcr]="" [measure-pcr]="" [pcr]="_none" [devices]="_devices" [rootfs]="_rootfs" [rootfs-data]="_rootfs_data" + [esp-free-space]="_esp_free_space" [force]="" ) opts_long="" @@ -4230,13 +4118,13 @@ --default-snapshot) arg_default_snapshot=1; shift ;; --ask-key|--ask-pin|--ask-pw) arg_ask_key_pin_or_pw=1; shift ;; --method) arg_method="$2"; shift 2 ;; - --signed-policy) arg_signed_policy=1; shift ;; --no-measure-pcr) arg_no_measure_pcr=1; shift ;; --measure-pcr) arg_measure_pcr=1; shift ;; --pcr) arg_pcr="$2"; shift 2 ;; --devices) IFS=',' read -r -a tracked_devices <<<"$2"; shift 2 ;; --rootfs) arg_rootfs="$2"; shift 2 ;; --rootfs-data) arg_rootfs_data="$2"; shift 2 ;; + --esp-free-space) arg_esp_free_space="$2"; shift 2 ;; --force) arg_force=1; shift ;; --) shift ; break ;; *) echo "Internal error!" ; exit 1 ;; @@ -4343,6 +4231,9 @@ # If there is a config file, load it. If not, but the bootloader was # installed, create a default one (using the CLI arguments). If not, # use some default values. +# +# When loading a config file with missing values, the load code will +# use default values. if is_config_file; then load_config_file elif is_installed; then diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdbootutil-1+git20260303.90d816d/sdbootutil.spec new/sdbootutil-1+git20260311.73a155b/sdbootutil.spec --- old/sdbootutil-1+git20260303.90d816d/sdbootutil.spec 2026-03-03 11:19:37.000000000 +0100 +++ new/sdbootutil-1+git20260311.73a155b/sdbootutil.spec 2026-03-11 15:54:50.000000000 +0100 @@ -36,7 +36,6 @@ Requires: jq Requires: keyutils Requires: openssl -Requires: pcr-oracle Requires: qrencode Requires: sed Requires: (%{name}-snapper if (snapper and btrfsprogs)) ++++++ sdbootutil.obsinfo ++++++ --- /var/tmp/diff_new_pack.87sutP/_old 2026-03-12 22:22:20.002424473 +0100 +++ /var/tmp/diff_new_pack.87sutP/_new 2026-03-12 22:22:20.006424638 +0100 @@ -1,5 +1,5 @@ name: sdbootutil -version: 1+git20260303.90d816d -mtime: 1772533177 -commit: 90d816d884019f7ffa0dc7b8e1dc866bfe7f922a +version: 1+git20260311.73a155b +mtime: 1773240890 +commit: 73a155bdc01e5e2e29de7d2e30f80e63eb713acd
