Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package sdbootutil for openSUSE:Factory checked in at 2024-07-18 19:15:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/sdbootutil (Old) and /work/SRC/openSUSE:Factory/.sdbootutil.new.17339 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "sdbootutil" Thu Jul 18 19:15:13 2024 rev:24 rq:1188375 version:1+git20240718.6fbaad1 Changes: -------- --- /work/SRC/openSUSE:Factory/sdbootutil/sdbootutil.changes 2024-07-17 15:14:32.236723195 +0200 +++ /work/SRC/openSUSE:Factory/.sdbootutil.new.17339/sdbootutil.changes 2024-07-18 19:15:17.368604812 +0200 @@ -1,0 +2,18 @@ +Thu Jul 18 10:05:20 UTC 2024 - apla...@suse.com + +- Update to version 1+git20240718.6fbaad1: + * Enroll using pcr_oracle if pcrlock fails + * Fix loader.conf measurement + * Address some shellcheck issues + * Implement basic [un]enroll commands + * snapper: do not error if sdbootutil fails + +------------------------------------------------------------------- +Tue Jul 16 12:25:53 UTC 2024 - apla...@suse.com + +- Update to version 1+git20240716.bb40c38: + * Add --only-default option for list-entries command + * Turn off colors when the shell it not interactive + * Support portable installation of bootloader This is useful to create portable drives, so the bootloader entry isn't created permenantly. + +------------------------------------------------------------------- Old: ---- sdbootutil-1+git20240704.a2c5a26.obscpio New: ---- sdbootutil-1+git20240718.6fbaad1.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ sdbootutil.spec ++++++ --- /var/tmp/diff_new_pack.KMOSfE/_old 2024-07-18 19:15:19.440689232 +0200 +++ /var/tmp/diff_new_pack.KMOSfE/_new 2024-07-18 19:15:19.456689884 +0200 @@ -27,7 +27,7 @@ %define git_version %{nil} %endif Name: sdbootutil -Version: 1+git20240704.a2c5a26%{git_version} +Version: 1+git20240718.6fbaad1%{git_version} Release: 0 Summary: script to install shim with sd-boot License: MIT ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.KMOSfE/_old 2024-07-18 19:15:19.836705366 +0200 +++ /var/tmp/diff_new_pack.KMOSfE/_new 2024-07-18 19:15:19.868706670 +0200 @@ -3,6 +3,6 @@ <param name="url">https://github.com/lnussel/sdbootutil.git</param> <param name="changesrevision">708592a5033bb41d14e378172466ae9e90dfb3c4</param></service><service name="tar_scm"> <param name="url">https://github.com/openSUSE/sdbootutil.git</param> - <param name="changesrevision">a2c5a26866076afd91446dea57a44ac91746a4bf</param></service></servicedata> + <param name="changesrevision">6fbaad1a48cb99629775d6584cdbc90de826a1d8</param></service></servicedata> (No newline at EOF) ++++++ sdbootutil-1+git20240704.a2c5a26.obscpio -> sdbootutil-1+git20240718.6fbaad1.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdbootutil-1+git20240704.a2c5a26/10-sdbootutil.snapper new/sdbootutil-1+git20240718.6fbaad1/10-sdbootutil.snapper --- old/sdbootutil-1+git20240704.a2c5a26/10-sdbootutil.snapper 2024-07-04 14:00:45.000000000 +0200 +++ new/sdbootutil-1+git20240718.6fbaad1/10-sdbootutil.snapper 2024-07-18 12:02:41.000000000 +0200 @@ -21,7 +21,7 @@ is_transactional && return 0 - /usr/bin/sdbootutil add-all-kernels "$num" + /usr/bin/sdbootutil add-all-kernels "$num" || : } delete_snapshot() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdbootutil-1+git20240704.a2c5a26/sdbootutil new/sdbootutil-1+git20240718.6fbaad1/sdbootutil --- old/sdbootutil-1+git20240704.a2c5a26/sdbootutil 2024-07-04 14:00:45.000000000 +0200 +++ new/sdbootutil-1+git20240718.6fbaad1/sdbootutil 2024-07-18 12:02:41.000000000 +0200 @@ -20,14 +20,23 @@ arg_no_variables= arg_no_reuse_initrd= arg_no_random_seed= -arg_ask_pin= +arg_portable= +arg_only_default= +arg_ask_pin_or_pw= +arg_method= +arg_signed_policy= have_snapshots= # for x in vmlinuz image vmlinux linux bzImage uImage Image zImage; do image= color_red= color_end= -if [ "$SYSTEMD_COLORS" != "false" ] && [ "$SYSTEMD_COLORS" != "0" ]; then +# The shell is interactive and the colors haven't been disabled forcefully +# or the shell is not interactive but the colors have been forcefully enabled +if { [[ "$-" == *i* ]] \ + && [ "$SYSTEMD_COLORS" != "false" ] && [ "$SYSTEMD_COLORS" != "0" ]; } \ + || [[ "$-" != *i* ]] \ + && { [ "$SYSTEMD_COLORS" = "true" ] || [ "$SYSTEMD_COLORS" = "1" ]; }; then color_red="\e[31m" color_green="\e[32m" color_yellow="\e[33m" @@ -38,8 +47,11 @@ # State file for transactional systems state_file="/var/lib/misc/transactional-update.state" +update_initrd= update_predictions= +luks2_devices=() + rollback=() tmpdir=$(mktemp -d -t sdbootutil.XXXXXX) @@ -76,7 +88,13 @@ --entry-keys Comma separated list of keys --no-variables Do not update UEFI variables --no-reuse-initrd Always regenerate initrd + --portable Handle bootloader on portable devices + --only-default Only list the default entry --ask-pin Ask recovery PIN for re-enrollment + Ask TPM2 PIN when initial enrollment + --ask-pw Ask password when initial enrollment + --method "tpm2", "tpm2+pin", "fido2", "password" + --signed-policy Use signed policy for TPM2 enrollment -v, --verbose More verbose output -h, --help This screen @@ -132,6 +150,14 @@ force-update Update the bootloader in any case + enroll + Enroll a TPM2 (+PIN), a FIDO2 key or a password for + all devices + + unenroll + Unenroll a TPM2 (+PIN), a FIDO2 key or a password for + all devices + update-predictions Update TPM2 predictions @@ -143,6 +169,8 @@ Variables: SYSTEMD_COLORS Set 0 to disable colored output PIN Recovery PIN / password (re-enrollment) + PIN TPM2 PIN (initial enrollment; %u:pin) + PW Password (initial enrollment; %u:pw) EOF exit 0 } @@ -378,7 +406,8 @@ { local kernel_version="${1:?}" local snapshot="$2" - local id="$(entry_conf_file "$kernel_version" "$snapshot")" + local id + id="$(entry_conf_file "$kernel_version" "$snapshot")" update_entries_for_snapshot "$snapshot" @@ -438,7 +467,8 @@ local kernel_version="$2" [ -n "$kernel_version" ] || err "Missing kernel version" settle_entry_token "${snapshot}" - local id="$(entry_conf_file "$kernel_version" "$snapshot")" + local id + id="$(entry_conf_file "$kernel_version" "$snapshot")" run_command_output bootctl unlink "$id" # This action will require to update the PCR predictions @@ -508,7 +538,7 @@ for file in "${os_release_files[@]}"; do [ -f "$file" ] || continue - eval $(sed -ne '/^[A-Z_]\+=/s/^/os_release_/p' < "$file") + eval "$(sed -ne '/^[A-Z_]\+=/s/^/os_release_/p' < "$file")" break done } @@ -576,10 +606,10 @@ local snapshot_dir="$1" # don't mount if we are within a transactional-update shell - [ -z "$TRANSACTIONAL_UPDATE" ] || return 0 + [ -z "$TRANSACTIONAL_UPDATE" ] || return 0 IFS=',' read -ra fields <<< \ - $(findmnt --tab-file "${snapshot_dir}/etc/fstab" --noheadings --nofsroot --output OPTIONS /etc | sed 's#/sysroot##g' | sed 's#:/etc,#:'"${snapshot_dir}"'/etc,#g') + "$(findmnt --tab-file "${snapshot_dir}/etc/fstab" --noheadings --nofsroot --output OPTIONS /etc | sed 's#/sysroot##g' | sed 's#:/etc,#:'"${snapshot_dir}"'/etc,#g')" local lower="" local upper="" @@ -596,7 +626,7 @@ { local snapshot_dir="$1" # don't umount if we are within a transactional-update shell - [ -z "$TRANSACTIONAL_UPDATE" ] || return 0 + [ -z "$TRANSACTIONAL_UPDATE" ] || return 0 umount "${snapshot_dir}/etc" } @@ -648,7 +678,7 @@ local id while read -r id; do free_space="$(boot_free_space)" - if [ "$total_size" -gt "$free_space" ]; then + if [ "$total_size" -gt "$free_space" ]; then log_info "Removing boot entry $id" bootctl unlink "$id" else @@ -812,6 +842,7 @@ list_entries() { + # The password is read from "cryptenroll" kernel keyring if [ ! -s "$entryfile" ]; then if [ -n "$1" ]; then update_entries_for_snapshot "$1" @@ -827,6 +858,8 @@ color= if [ "$isdefault" = "true" ]; then color="$color_bu" + elif [ -n "$arg_only_default" ]; then + continue fi if [ "$isreported" = "false" ]; then color="$color${color_green}" @@ -868,7 +901,8 @@ local kernel_version="$2" [ -n "$kernel_version" ] || err "Missing kernel version" settle_entry_token "${snapshot}" - local id="$(entry_conf_file "$kernel_version" "$snapshot")" + local id + id="$(entry_conf_file "$kernel_version" "$snapshot")" local conf conf="$(find_conf_file "${kernel_version}" "${snapshot}")" @@ -882,7 +916,7 @@ *) continue ;; esac - if [ -z "$arg_entry_keys" ] || [[ ${arg_entry_keys[@]} =~ "all" ]] || [[ ${arg_entry_keys[@]} =~ "$k" ]]; then + if [ -z "$arg_entry_keys" ] || [[ ${arg_entry_keys[*]} == *"all"* ]] || [[ ${arg_entry_keys[*]} == *"$k"* ]]; then echo -e "$k\t$v" fi done < "$conf" @@ -1079,9 +1113,9 @@ calc_chksum() { - # shellcheck disable=SC2046 - set -- $(sha1sum "$1") - chksum="$1" + # shellcheck disable=SC2046 + set -- $(sha1sum "$1") + chksum="$1" } # map with kernel version as key and checksum as value @@ -1283,7 +1317,8 @@ { local prefix="" [ -z "$have_snapshots" ] || prefix="/.snapshots/${1-$root_snapshot}/snapshot" - local grub2="$prefix/usr/share/efi/$(uname -m)/grub.efi" + local grub2 + grub2="$prefix/usr/share/efi/$(uname -m)/grub.efi" [ -e "$grub2" ] || grub2="$prefix/usr/share/grub2/$(uname -m)-efi/grub.efi" echo "$grub2" } @@ -1349,7 +1384,7 @@ log_info "Installing $bldr_name with shim into $boot_root" entry="$boot_dst/shim.efi" for i in MokManager shim; do - install -p -D "$prefix$shimdir/$i.efi" "$boot_root$boot_dst/$i.efi" + [ -n "$arg_portable" ] || install -p -D "$prefix$shimdir/$i.efi" "$boot_root$boot_dst/$i.efi" done install -p -D "$bootloader" "$boot_root$boot_dst/grub.efi" @@ -1361,14 +1396,15 @@ else log_info "Installing $bldr_name into $boot_root" entry="$boot_dst/${bootloader##*/}" - install -p -D "$bootloader" "$boot_root$entry" + [ -n "$arg_portable" ] || install -p -D "$bootloader" "$boot_root$entry" install -p -D "$bootloader" "$boot_root/EFI/BOOT/BOOT${firmware_arch^^}.EFI" fi + # this is for shim to create the entry if missing - echo "${entry##*/},openSUSE Boot Manager" | { echo -ne "\xff\xfe"; iconv -f ascii -t ucs-2le; } > "$boot_root/$boot_dst/boot.csv" + [ -n "$arg_portable" ] || echo "${entry##*/},openSUSE Boot Manager" | { echo -ne "\xff\xfe"; iconv -f ascii -t ucs-2le; } > "$boot_root$boot_dst/boot.csv" mkdir -p "$boot_root/$entry_token" - echo "$entry_token" > "$boot_root/$boot_dst/installed_by_sdbootutil" + echo "$entry_token" > "$boot_root$boot_dst/installed_by_sdbootutil" mkdir -p "/etc/kernel" [ -s /etc/kernel/entry-token ] || echo "$entry_token" > /etc/kernel/entry-token update_random_seed @@ -1385,7 +1421,7 @@ # # NOTE: if this file change, update the linearized # version in pcrlock_grub2_exec_cmdline - [ -e "$boot_root$boot_dst/grub.cfg" ] || cat > "$boot_root$boot_dst/grub.cfg" <<-EOF + [ -e "$boot_root$boot_dst/grub.cfg" ] || cat > "$boot_root$boot_dst/grub.cfg" <<-EOF timeout=8 function load_video { # A load_video call is added for each bls entry @@ -1402,7 +1438,7 @@ fi # Create boot menu entry if it does not exist - [ -n "$arg_no_variables" ] || efibootmgr | grep -q 'Boot.*openSUSE Boot Manager' || efibootmgr -q --create --disk "$drive" --part "$partno" --label "openSUSE Boot Manager" --loader "$entry" || true + [ -n "$arg_no_variables" ] || [ -n "$arg_portable" ] || efibootmgr | grep -q 'Boot.*openSUSE Boot Manager' || efibootmgr -q --create --disk "$drive" --part "$partno" --label "openSUSE Boot Manager" --loader "$entry" || true # This action will require to update the PCR predictions update_predictions=1 @@ -1485,19 +1521,26 @@ have_pcrlock() { - [ -e /usr/lib/systemd/systemd-pcrlock ] + [ -e /usr/bin/systemd-pcrlock ] || [ -e /usr/lib/systemd/systemd-pcrlock ] +} + +have_pcr_oracle() +{ + [ -e /usr/bin/pcr-oracle ] } pcrlock() { - SYSTEMD_LOG_LEVEL="${SYSTEMD_LOG_LEVEL:-warning}" /usr/lib/systemd/systemd-pcrlock "$@" + local pcrlock_cmd="/usr/bin/systemd-pcrlock" + [ -e "$pcrlock_cmd" ] || pcrlock_cmd="/usr/lib/systemd/systemd-pcrlock" + SYSTEMD_LOG_LEVEL="${SYSTEMD_LOG_LEVEL:-warning}" "$pcrlock_cmd" "$@" } is_pcr_oracle() { [ -e /etc/systemd/tpm2-pcr-public-key.pem ] && \ [ -e /etc/systemd/tpm2-pcr-private-key.pem ] && \ - [ -e /usr/bin/pcr-oracle ] + have_pcr_oracle } regex_entries_ids_for_prediction() @@ -1525,7 +1568,7 @@ done else while read -r id; do - if [ "${#snaphosts[@]}" -lt 3 ]; then + if [ "${#snapshots[@]}" -lt 3 ]; then snapshots[$id]=1 fi done < <(jq -r '.root|sort_by(.date)[-2:]|.[]|.number' "$snapperfile") @@ -1628,9 +1671,9 @@ locks+=( "$tmpdir/element-$n.pcrlock" ) done jq --slurp '{"records": [.[].records[0]]}' \ - ${locks[@]} \ + "${locks[@]}" \ > "/var/lib/pcrlock.d/710-grub2-kernel-initrd-entry.pcrlock.d/kernel-initrd-$suffix.pcrlock" - rm ${locks[@]} + rm "${locks[@]}" } pcrlock_grub2_exec_cmdline() @@ -1665,9 +1708,9 @@ rm "$tmpdir/line" done jq --slurp '{"records": [.[].records[0]]}' \ - ${locks[@]} \ + "${locks[@]}" \ > "/var/lib/pcrlock.d/650-grub2-entry-exec-cmdline.pcrlock.d/exec-cmdline-$suffix.pcrlock" - rm ${locks[@]} + rm "${locks[@]}" } pcrlock_grub2_entry_files() @@ -1692,18 +1735,18 @@ done < <(jq --raw-output 'map(.path) | .[]' "$entries") jq --slurp '{"records": [.[].records[0]]}' \ - ${locks[@]} \ + "${locks[@]}" \ > "/var/lib/pcrlock.d/643-grub2-entry-files.pcrlock.d/generated$suffix.pcrlock" - rm ${locks[@]} + rm "${locks[@]}" } pcrlock_sdboot() { # 641-sdboot-loader-conf.pcrlock is not part of the pcrlock # standards - if [ -e "{$boot_root}/loader/loader.conf" ]; then + if [ -e "${boot_root}/loader/loader.conf" ]; then pcrlock \ - lock-raw "{$boot_root}/loader/loader.conf" \ + lock-raw "${boot_root}/loader/loader.conf" \ --pcr=5 \ --pcrlock=/var/lib/pcrlock.d/641-sdboot-loader-conf.pcrlock fi @@ -1843,17 +1886,19 @@ pcrlock lock-firmware-config # If secure boot is disabled, this can fail. There is patch # for the policy generation, and for the authority is planned - /usr/lib/systemd/systemd-pcrlock lock-secureboot-policy &> /dev/null || true - /usr/lib/systemd/systemd-pcrlock lock-secureboot-authority &> /dev/null || true + pcrlock lock-secureboot-policy &> /dev/null || true + pcrlock lock-secureboot-authority &> /dev/null || true # uses / by default pcrlock lock-gpt # 630-shim-efi-application is not part of the pcrlock standards # TODO: move to shim-pcrlock.rpm + local entry="shim.efi" + [ -f "${boot_root}${boot_dst}/${entry}" ] || entry="BOOT${firmware_arch^^}.EFI" pcrlock \ lock-pe \ --pcrlock=/var/lib/pcrlock.d/630-shim-efi-application.pcrlock.d/generated.pcrlock \ - "${boot_root}${boot_dst}/shim.efi" + "${boot_root}${boot_dst}/${entry}" # 640-boot-loader-efi-application is not part of the pcrlock # standards @@ -1872,21 +1917,23 @@ # If the prediction fails, the system will ask for a password, # but we can do a re-enrollment using the recovery PIN. To - # register a recovery PIN disk-encryption-tool will call this - # script deploying in the @u keyring a "cryptsetup" entry. - # For re-enrollments we can use the same entry, the PIN - # environment variable, or the --ask-pin parameter. + # register a recovery PIN the installer (disk-encryption-tool, + # YaST) will call this script deploying in the %u keyring + # "cryptenroll" entry. For re-enrollments we can use the same + # entry, the PIN environment variable, or the --ask-pin + # parameter. local pin local extra=() - local keyid="$(keyctl id %user:cryptenroll 2> /dev/null)" || true + local keyid + keyid="$(keyctl id %user:cryptenroll 2> /dev/null)" || true if [ -n "$keyid" ]; then pin="$(keyctl pipe "$keyid")" extra=("--recovery-pin=yes") elif [ -n "$PIN" ]; then pin="$PIN" extra=("--recovery-pin=yes") - elif [ -n "$arg_ask_pin" ]; then - read -s -p "Recovery PIN: " pin + elif [ -n "$arg_ask_pin_or_pw" ]; then + read -r -s -p "Recovery PIN: " pin extra=("--recovery-pin=yes") fi @@ -1915,7 +1962,7 @@ 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) + done <<<"$(blkid -t TYPE=crypto_LUKS -o device)" } generate_tpm2_predictions_pcr_oracle() @@ -1974,7 +2021,7 @@ if is_pcr_oracle; then generate_tpm2_predictions_pcr_oracle elif have_pcrlock; then - [ -e /etc/sysconfig/fde-tools ] || { log_info "/etc/sysconfig/fde-tools not found. System not enrolled"; return 0; } + [ -e /etc/sysconfig/fde-tools ] || { log_info "/etc/sysconfig/fde-tools not found"; return 0; } # shellcheck disable=SC1091 . /etc/sysconfig/fde-tools @@ -1982,6 +2029,396 @@ fi } +have_luks2() +{ + [ "${#luks2_devices[@]}" -gt 0 ] +} + +detect_luks2() +{ + local dev fstype + [ -z "$luks2_devices" ] || return 0 + while read -r dev fstype; do + [ "$fstype" = 'crypto_LUKS' ] || continue + cryptsetup isLuks --type luks2 "$dev" || continue + luks2_devices+=("$dev") + done < <(lsblk --noheadings -o PATH,FSTYPE) + have_luks2 +} + +have_tpm2() +{ + [ -n "$(systemd-cryptenroll --tpm2-device=list 2>/dev/null)" ] +} + +have_fido2() +{ + [ -n "$(systemd-cryptenroll --fido2-device=list 2>/dev/null)" ] +} + +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 + local option="$1" + + local crypttab + crypttab="$(mktemp -t crypttab.XXXXXX)" + echo "# File created by sdbootutil. Comments will be removed" > "$crypttab" + + local name + local device + local key + local opts + while read -r name device key opts; do + [[ "$name" = \#* ]] && continue + if [[ "$opts" != *"$option"* ]]; then + [ -z "$opts" ] && opts="$option" || opts="$opts,$option" + # crypttab has changed so initrd needs to be updated + update_initrd=1 + fi + echo "$name $device ${key:-none} $opts" >> "$crypttab" + done < /etc/crypttab + + mv "$crypttab" /etc/crypttab + chmod 644 /etc/crypttab +} + +remove_crypttab_option() +{ + # Similar to add_crypttab_option, the option will be removed + # from all the entries + local option="$1" + + local crypttab + crypttab="$(mktemp -t crypttab.XXXXXX)" + echo "# File created by sdbootutil. Comments will be removed" > "$crypttab" + + local name + local device + local key + local opts + while read -r name device key opts; do + [[ "$name" = \#* ]] && continue + if [[ "$opts" == *"$option"* ]]; then + opts="${opts//,$option}" + opts="${opts//$option}" + # crypttab has changed so initrd needs to be updated + update_initrd=1 + fi + echo "$name $device ${key:-none} $opts" >> "$crypttab" + done < /etc/crypttab + + mv "$crypttab" /etc/crypttab + chmod 644 /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 +} + +enroll_pcrlock() +{ + local dev="$1" + local tpm_pin="$2" + local extra_args=() + + if [ -z "$tpm_pin" ]; then + echo "Enrolling with TPM2 (pcrlock): $dev" + else + echo "Enrolling with TPM2+PIN (pcrlock): $dev" + extra_args+=(--tpm2-with-pin=1) + fi + + # The password is read from "cryptenroll" kernel keyring + # XXX: Wipe is separated by now (possible systemd bug) + systemd-cryptenroll \ + --wipe-slot=tpm2 \ + "$dev" + + # Note that the PCRs are now not stored in the LUKS2 header + NEWPIN="$tpm_pin" systemd-cryptenroll \ + --tpm2-device=auto \ + "${extra_args[@]}" \ + --tpm2-pcrlock=/var/lib/systemd/pcrlock.json \ + "$dev" +} + +enroll_pcroracle() +{ + local dev="$1" + local tpm_pin="$2" + local extra_args=() + + if [ -z "$tpm_pin" ]; then + echo "Enrolling with TPM2 (pcr-oracle): $dev" + else + echo "Enrolling with TPM2+PIN (pcr-oracle): $dev" + extra_args+=(--tpm2-with-pin=1) + fi + + # The password is read from "cryptenroll" kernel keyring + # XXX: Wipe is separated by now (possible systemd bug) + systemd-cryptenroll \ + --wipe-slot=tpm2 \ + "$dev" + + # Note that the PCRs are now not stored in the LUKS2 header + NEWPIN="$tpm_pin" systemd-cryptenroll \ + --tpm2-device=auto \ + "${extra_args[@]}" \ + --tpm2-public-key=/etc/systemd/tpm2-pcr-public-key.pem \ + --tpm2-public-key-pcrs="${FDE_SEAL_PCR_LIST}" \ + "$dev" +} + +enroll_fido2() +{ + local dev="$1" + + echo "Enrolling with FIDO2: $dev" + + # The password is read from "cryptenroll" kernel keyring + # XXX: Wipe is separated by now (possible systemd bug) + systemd-cryptenroll \ + --wipe-slot=fido2 \ + "$dev" + + # The password is read from "cryptenroll" kernel keyring + systemd-cryptenroll --fido2-device=auto "$dev" +} + +enroll_password() +{ + local dev="$1" + local pw="$2" + + echo "Enrolling with password: $dev" + + # The password is read from "cryptenroll" kernel keyring + NEWPASSWORD="$pw" systemd-cryptenroll --wipe-slot=password --password "$dev" +} + +enroll_device() +{ + local dev="$1" + local pin_or_pw="$2" + + case "$arg_method" in + "tpm2"|"tpm2+pin") + if [ -z "$arg_signed_policy" ] && have_pcrlock; then + enroll_pcrlock "$dev" "$pin_or_pw" || { + echo "Enrollment with systemd-pcrlock failed" + echo "Re-trying with pcr-oracle" + unernroll_pcrlock + enroll_pcr_oracle "$dev" "$pin_or_pw" + } + elif have_pcr_oracle; then + enroll_pcr_oracle "$dev" "$pin_or_pw" + else + log_info "No TMP2 enrollment mechanism found" + fi + ;; + + "fido2") + enroll_fido2 "$dev" + ;; + + "password") + enroll_password "$dev" "$pin_or_pw" + ;; + + *) + err "Unexpected parameter for --method=: $arg_method" + ;; + esac +} + +enroll() +{ + [ -e /etc/crypttab ] || { log_info "/etc/crypttab not found. No encrypted devices?"; return 0; } + [ -e /usr/bin/systemd-cryptenroll ] || { log_info "systemd-cryptenroll not found"; return 0; } + detect_luks2 || { log_info "No LUKS2 devices found"; return 0; } + + have_pcrlock || have_pcr_oracle || err "Nor systemd-pcrlock not 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" + add_crypttab_option 'tpm2-device=auto' + ;; + + "fido2") + have_fido2 || err "No FIDO2 key found" + add_crypttab_option 'fido2-device=auto' + ;; + esac + if [ "$update_initrd" = "1" ]; then + arg_no_reuse_initrd=1 + 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 [ -z "${FDE_SEAL_PCR_LIST}" ]; then + if is_sdboot; then + FDE_SEAL_PCR_LIST="0,2,4,7,9" + elif is_grub2; then + FDE_SEAL_PCR_LIST="0,2,4,7,8,9" + else + err "Bootloader not detected" + fi + fi + [ -e /etc/sysconfig/fde-tools ] || echo "FDE_SEAL_PCR_LIST=${FDE_SEAL_PCR_LIST}" > /etc/sysconfig/fde-tools + # shellcheck disable=SC1091 + . /etc/sysconfig/fde-tools + + if ! have_pcrlock && have_pcr_oracle; then + 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 "cryptenroll" entry + generate_tpm2_predictions + fi + + # For the PIN (tpm2+pin) or password (password), we can get it + # from the %u keyring "pin" or "pw" entry, the PIN or PW + # environment variable, or introduced by the user + local pin_or_pw keyid + if [ "$arg_method" = "tpm2+pin" ]; then + keyid="$(keyctl id %user:pin 2> /dev/null)" || true + if [ -n "$keyid" ]; then + pin_or_pw="$(keyctl pipe "$keyid")" + elif [ -n "$PIN" ]; then + pin_or_pw="$PIN" + elif [ -n "$arg_ask_pin_or_pw" ]; then + read -r -s -p "TPM2 PIN: " pin_or_pw + else + err "Use %u:pin, PIN or --ask-pin to provide the TPM2 PIN" + fi + elif [ "$arg_method" = "password" ]; then + local keyid + keyid="$(keyctl id %user:pw 2> /dev/null)" || true + if [ -n "$keyid" ]; then + pin_or_pw="$(keyctl pipe "$keyid")" + elif [ -n "$PW" ]; then + pin_or_pw="$PW" + elif [ -n "$arg_ask_pin_or_pw" ]; then + read -r -s -p "Password: " pin_or_pw + else + err "Use %u:pw, PW or --ask-pw to provide the password" + fi + fi + + for dev in "${luks2_devices[@]}"; do + enroll_device "$dev" "$pin_or_pw" + done +} + +unenroll_pcrlock() +{ + pcrlock remove-policy &> /dev/null || true + rm -fr /var/lib/pcrlock.d + rm -f /var/lib/systemd/pcrlock.json + rm -f "${boot_root}${boot_dst}/pcrlock.json" +} + +unenroll_pcr_oracle() +{ + rm -f /etc/systemd/tpm2-pcr-private-key.pem + rm -f /etc/systemd/tpm2-pcr-public-key.pem + rm -f /etc/systemd/tpm2-pcr-signature.json + rm -f "${boot_root}${boot_dst}/tpm2-pcr-public-key.pem" + rm -f "${boot_root}${boot_dst}/tpm2-pcr-signature.json" +} + +unenroll_device() +{ + local dev="$1" + + case "$arg_method" in + "tpm2"|"tpm2+pin") + systemd-cryptenroll \ + --wipe-slot=tpm2 \ + "$dev" + ;; + + "fido2") + systemd-cryptenroll \ + --wipe-slot=fido2 \ + "$dev" + ;; + + "password") + systemd-cryptenroll \ + --wipe-slot=password \ + "$dev" + ;; + + *) + err "Unexpected parameter for --method=: $arg_method" + ;; + esac +} + +unenroll() +{ + [ -e /etc/crypttab ] || { log_info "/etc/crypttab not found. No encrypted devices?"; return 0; } + [ -e /usr/bin/systemd-cryptenroll ] || { log_info "systemd-cryptenroll not found"; return 0; } + detect_luks2 || { log_info "No LUKS2 devices found"; return 0; } + + # Prepare /etc/crypttab and update initrd if required + case "$arg_method" in + "tpm2"|"tpm2+pin") + have_tpm2 || err "No TPM2 found found" + remove_crypttab_option 'tpm2-device=auto' + ;; + + "fido2") + have_fido2 || err "No FIDO2 key found" + remove_crypttab_option 'fido2-device=auto' + ;; + esac + if [ "$update_initrd" = "1" ]; then + arg_no_reuse_initrd=1 + 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 + # Maybe we can remove fde-tools, but it is OK to keep + # it, as is a user configuration file + # [ ! -e /etc/sysconfig/fde-tools ] || rm /etc/sysconfig/fde-tools + unenroll_pcrlock + unenroll_pcr_oracle + fi + + for dev in "${luks2_devices[@]}"; do + unenroll_device "$dev" + done +} + bootloader_name() { if is_sdboot "${1-$root_snapshot}"; then @@ -2011,12 +2448,12 @@ ####### main ####### -getopttmp=$(getopt -o hc:v --long help,flicker,verbose,esp-path:,entry-token:,arch:,image:,entry-keys:,no-variables,no-reuse-initrd,no-random-seed,ask-pin,all -n "${0##*/}" -- "$@") +getopttmp=$(getopt -o hc:v --long help,flicker,verbose,esp-path:,entry-token:,arch:,image:,entry-keys:,no-variables,no-reuse-initrd,no-random-seed,all,portable,only-default,ask-pin,ask-pw,method:,signed-policy -n "${0##*/}" -- "$@") eval set -- "$getopttmp" while true ; do - case "$1" in - -h|--help) helpandquit ;; + case "$1" in + -h|--help) helpandquit ;; --flicker) dialog_altenate_screen=--keep-tite; shift ;; -v|--verbose) verbose=$((++verbose)); shift ;; --esp-path) arg_esp_path="$2"; shift 2 ;; @@ -2027,14 +2464,18 @@ --no-variables) arg_no_variables=1; shift ;; --no-reuse-initrd) arg_no_reuse_initrd=1; shift ;; --no-random-seed) arg_no_random_seed=1; shift ;; - --ask-pin) arg_ask_pin=1; shift ;; --all) arg_all_entries=1; shift ;; - --) shift ; break ;; - *) echo "Internal error!" ; exit 1 ;; - esac + --portable) arg_portable=1; shift ;; + --only-default) arg_only_default=1; shift ;; + --ask-pin|--ask-pw) arg_ask_pin_or_pw=1; shift ;; + --method) arg_method="$2"; shift 2 ;; + --signed-policy) arg_signed_policy=1; shift ;; + --) shift ; break ;; + *) echo "Internal error!" ; exit 1 ;; + esac done -if [ -z "$SYSTEMD_LOG_LEVEL" -a -n "$verbose" ]; then +if [ -z "$SYSTEMD_LOG_LEVEL" ] && [ -n "$verbose" ]; then if [ "$verbose" -gt 1 ]; then SYSTEMD_LOG_LEVEL=debug else @@ -2044,7 +2485,7 @@ fi case "$1" in - install|needs-update|update|force-update|add-kernel|remove-kernel|set-default-snapshot|add-all-kernels|mkinitrd|remove-all-kernels|is-installed|list-snapshots|list-entries|list-kernels|show-entry|is-bootable|update-predictions|bootloader) ;; + install|needs-update|update|force-update|add-kernel|remove-kernel|set-default-snapshot|add-all-kernels|mkinitrd|remove-all-kernels|is-installed|list-snapshots|list-entries|list-kernels|show-entry|is-bootable|enroll|unenroll|update-predictions|bootloader) ;; kernels|snapshots|entries|"") stty_size; interactive=1 ;; *) err "unknown command $1" ;; esac @@ -2052,6 +2493,7 @@ [ -n "$arg_esp_path" ] && export SYSTEMD_ESP_PATH="$arg_esp_path" # XXX: bootctl should have json output for that too +# shellcheck disable=SC2016 eval "$(bootctl 2>/dev/null | sed -ne 's/Firmware Arch: *\(\w\+\)/firmware_arch="\1"/p;s/ *token: *\(\w\+\)/entry_token="\1"/p;s, *\$BOOT: *\([^ ]\+\).*,boot_root="\1",p')" read -r root_uuid root_device < <(findmnt / -v -r -n -o UUID,SOURCE) root_subvol="" @@ -2084,7 +2526,13 @@ esac # XXX: Unify both in /EFI/opensuse? -if is_sdboot; then +if [ -n "$arg_portable" ]; then + if [ ! -d "$boot_root/EFI/systemd" ] && [ ! -d "$boot_root/EFI/opensuse" ]; then + boot_dst="/EFI/BOOT" + else + err "Bootloader is already installed permanently" + fi +elif is_sdboot; then boot_dst="/EFI/systemd" elif is_grub2; then boot_dst="/EFI/opensuse" @@ -2139,6 +2587,10 @@ show_entry_fields "${3:-$root_snapshot}" "$2" elif [ "$1" = "is-bootable" ]; then is_bootable "${2:-$root_snapshot}" +elif [ "$1" = "enroll" ]; then + enroll +elif [ "$1" = "unenroll" ]; then + unenroll elif [ "$1" = "update-predictions" ]; then update_predictions=1 elif [ "$1" = "kernels" ]; then ++++++ sdbootutil.obsinfo ++++++ --- /var/tmp/diff_new_pack.KMOSfE/_old 2024-07-18 19:15:20.144717915 +0200 +++ /var/tmp/diff_new_pack.KMOSfE/_new 2024-07-18 19:15:20.144717915 +0200 @@ -1,5 +1,5 @@ name: sdbootutil -version: 1+git20240704.a2c5a26 -mtime: 1720094445 -commit: a2c5a26866076afd91446dea57a44ac91746a4bf +version: 1+git20240718.6fbaad1 +mtime: 1721296961 +commit: 6fbaad1a48cb99629775d6584cdbc90de826a1d8