Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 31 additions & 48 deletions mkosi/resources/mkosi-obs/mkosi.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Second stage of build:
# - signed hashes are in cpio archive in SOURCES/ together with artifacts from previous build
# - if there are PCR policies to attach, do so and then prepare the hashes of the UKIs themselves
# - if not, attach the signatures to the UKI(s) with pesign
# - if not, attach the signatures to the UKI(s) with systemd-sbsign
# - remove shasums of previous artifacts, given we are re-creating them after this step
# - place artifacts from previous builds and signed UKI in output directory

Expand All @@ -25,16 +25,6 @@ popd

OUTPUTDIR=/work/src/usr/src/packages/OTHER

# OBS signs a hash without certificate information so it cannot simply be
# attached to the PE binaries, certificate metadata has to be provided separately
# so we need to create a certutil db and import the certificate manually
rm -rf nss-db
mkdir nss-db
nss_db="$PWD/nss-db"
certutil -N -d sql:"$nss_db" --empty-password
certutil -A -d sql:"$nss_db" -n cert -t CT,CT,CT -i /usr/src/packages/SOURCES/_projectcert.crt
openssl x509 -inform PEM -in /usr/src/packages/SOURCES/_projectcert.crt -outform DER -out _projectcert.cer

cp -r /usr/src/packages/SOURCES/"$IMAGE_ID"* /usr/src/packages/SOURCES/*raw* /usr/src/packages/SOURCES/*efi* "$OUTPUTDIR" || true
rm -f "$OUTPUTDIR/hashes.cpio.rsasign*" "$OUTPUTDIR"/*.sha*

Expand All @@ -48,16 +38,13 @@ while read -r SIG; do
DEST="$OUTPUTDIR/${SIG#*/}"
DEST="${DEST%%.sig}"

# ensure the EFI hash matches before and after attaching the signature
old_hash=$(pesign -n sql:"$nss_db" -h -P -i "/usr/src/packages/SOURCES/$infile" | cut -d' ' -f1)

pesign -n sql:"$nss_db" --force -c cert -i "/usr/src/packages/SOURCES/$infile" -o "$DEST" -d sha256 -I "$sattrs" -R "hashes/ukis/$SIG"

new_hash=$(pesign -n sql:"$nss_db" -h -i "$DEST" | cut -d' ' -f1)
if [ "$old_hash" != "$new_hash" ]; then
echo "Pesign hash mismatch error: $old_hash $new_hash"
exit 1
fi
systemd-sbsign \
sign \
--certificate /usr/src/packages/SOURCES/_projectcert.crt \
--output "$DEST" \
--signed-data "$sattrs" \
--signed-data-signature "hashes/ukis/$SIG" \
"/usr/src/packages/SOURCES/$infile"

rm -f "$(basename "${infile}").sattrs" "$SIG" "$infile"

Expand Down Expand Up @@ -114,16 +101,13 @@ while read -r BOOTLOADER; do
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
systemd-sbsign \
sign \
--certificate /usr/src/packages/SOURCES/_projectcert.crt \
--output "$signed" \
--signed-data "hashes/bootloaders/${BOOTLOADER%.sig}" \
--signed-data-signature "hashes/bootloaders/${BOOTLOADER}" \
"$unsigned"

mcopy -o -i "${ddi}@@${offset}" "$signed" "::$dest"

Expand All @@ -133,7 +117,6 @@ while read -r BOOTLOADER; do
fi
done < <(find "hashes/bootloaders/$(basename "$ddi")/" -type f -iname '*.efi.sig' -printf '%P\n')
rm -rf hashes/bootloaders
rm -rf nss-db

# Third step: if there are PCR policy signatures, rebuild the JSON
# blobs with the attached signatures
Expand Down Expand Up @@ -162,16 +145,18 @@ while read -r SIG; do
done < <(find hashes/pcrs -type f -name '*.sig')
rm -rf hashes/pcrs

mkdir -p "$nss_db"
certutil -N -d sql:"$nss_db" --empty-password

# Fourth step: now that the JSON blob is rebuilt, merge it in the UKI
while read -r PCRS; do
uki="${PCRS%.pcrs.sig}.efi"
ukify --json=short --pcrsig "@$PCRS" --join-pcrsig "$uki" --output "$uki.attached" build
mv "$uki.attached" "$uki"
mkdir -p hashes/ukis
pesign --force -n sql:"$nss_db" -i "$uki" -E "hashes/ukis/$(basename "$uki")"
systemd-sbsign \
sign \
--certificate /usr/src/packages/SOURCES/_projectcert.crt \
--output "hashes/ukis/$(basename "$uki")" \
--prepare-offline-signing \
"$uki"
done < <(find "$OUTPUTDIR" -type f -name '*.pcrs.sig')
rm -f "$OUTPUTDIR"/*.pcrs*

Expand Down Expand Up @@ -220,18 +205,16 @@ done < <(find hashes/roothashes -type f -name '*.sig')
rm -rf hashes/roothashes

# Sixth step: prepare EFI authvars for self-enrollment
# as implemented by https://github.com/openSUSE/pesign-obs-integration/blob/master/pesign-repackage.spec.in#L162
while read -r SIG; do
test -x /usr/lib/rpm/pesign/kernel-sign-file || break
f="${SIG%.sig}"
/usr/lib/rpm/pesign/kernel-sign-file -N -P -d -C _projectcert.cer -i pkcs7 -s "$SIG" sha256 _projectcert.cer "$f"
fbase="${f##*/}"
fbase="${fbase%.auth}"
fbase="${fbase%%-*}"
perl -0777 -npe 's/\A(?:[\040-\176]\0)+.{18}\0\0.{14}\0\0//s' < "$f" > "$f.orig"
sign-efi-sig-list -t "${SOURCE_DATE_EPOCH:-$(date +%s)}" -i "$f.p7sd" "$fbase" "$f.orig" "$f.tmp"
mv "$f.tmp" "$f"
rm -f "$f.p7s" "$f.p7sd" "$f.orig"
db="$(basename "$SIG")"
db="${db%.auth.sig}"
systemd-sbsign \
sign-secure-boot-database \
--certificate /usr/src/packages/SOURCES/_projectcert.crt \
--signed-data "${SIG%.sig}" \
--signed-data-signature "$SIG" \
--secure-boot-database "$db" \
--output "${SIG%.sig}"
done < <(find hashes/authvars -type f -name '*.auth.sig')
declare -a AUTHVARS
mapfile -t AUTHVARS < <(find hashes/authvars -type f -name "*.auth")
Expand Down Expand Up @@ -279,4 +262,4 @@ if [ -d hashes ]; then
cpio -t <"$OUTPUTDIR/hashes.cpio.rsasign"
fi

rm -rf hashes "$nss_db"
rm -rf hashes
47 changes: 29 additions & 18 deletions mkosi/resources/mkosi-obs/mkosi.postoutput
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# End of first stage of build:
# - built UKI is in $OUTPUTDIR
# - get PCR policy digests if any, or PE hash(es) with pesign
# - get PCR policy digests if any, or PE hash(es) with systemd-sbsign
# - pack them up in a cpio as OBS expects and store them in OTHER/
# - create minimal recipe for second stage that will continue from here

Expand All @@ -23,12 +23,6 @@ if ((${#UKIS[@]} == 0)) && ((${#KERNELS[@]} == 0)) && ((${#ROOTHASHES[@]} == 0))
exit 0
fi

nss_db="$PWD/nss-db"
# certutil will fail if it's called twice
rm -rf "$nss_db"
mkdir -p "$nss_db" hashes
certutil -N -d sql:"$nss_db" --empty-password

# When a single build has multiple images, postoutput is called for each image,
# so make sure the hashes.cpio from the previous stages gets its content preserved
if [ -f /usr/src/packages/OTHER/hashes.cpio.rsasign ]; then
Expand All @@ -46,14 +40,24 @@ for f in "${UKIS[@]}"; do
done < <(jq -r 'to_entries[] | .value[].pol' <"${OUTPUTDIR}/${f%.efi}.pcrs")
else
mkdir -p "$(dirname "hashes/ukis/$f")"
pesign --force -n sql:"$nss_db" -i "${OUTPUTDIR}/${f}" -E "hashes/ukis/$f"
systemd-sbsign \
sign \
--certificate /usr/src/packages/SOURCES/_projectcert.crt \
--output "hashes/ukis/$f" \
--prepare-offline-signing \
"${OUTPUTDIR}/${f}"
fi
done

for f in "${KERNELS[@]}"; do
test -f "${OUTPUTDIR}/${f}" || continue
mkdir -p "$(dirname "hashes/kernels/$f")"
pesign --force -n sql:"$nss_db" -i "${OUTPUTDIR}/${f}" -E "hashes/kernels/$f"
systemd-sbsign \
sign \
--certificate /usr/src/packages/SOURCES/_projectcert.crt \
--output "hashes/kernels/$f" \
--prepare-offline-signing \
"${OUTPUTDIR}/${f}"
done

repart_dir="$(jq -r '.RepartDirectories[-1]' "$MKOSI_CONFIG")"
Expand Down Expand Up @@ -92,7 +96,12 @@ for ddi in "${DDIS[@]}"; do

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"
systemd-sbsign \
sign \
--certificate /usr/src/packages/SOURCES/_projectcert.crt \
--output "hashes/bootloaders/$(basename "${ddi%.zst}")/$BOOTLOADER" \
--prepare-offline-signing \
"$BOOTLOADER"
done < <(find EFI -type f -iname '*.efi')

if [[ $ddi == *.zst ]]; then
Expand All @@ -105,22 +114,24 @@ done
if ((${#DDIS[@]} > 0)); then
mkdir -p hashes/authvars
pushd hashes/authvars 2>/dev/null
# Same as the GUID used by bootctl
guid=a5c059a1-94e4-4aa7-87b5-ab155c2bf072
cert-to-efi-sig-list -g "$guid" /usr/src/packages/SOURCES/_projectcert.crt db.esl
cp db.esl KEK.esl
cp db.esl PK.esl
for i in *.esl; do
sign-efi-sig-list -o -g "$guid" -t "${SOURCE_DATE_EPOCH:-$(date +%s)}" "${i%.esl}" "$i" "${i%.esl}.auth"

for db in PK KEK db; do
systemd-sbsign \
sign-secure-boot-database \
--certificate /usr/src/packages/SOURCES/_projectcert.crt \
--secure-boot-database "$db" \
--output "$db.auth" \
--prepare-offline-signing
done

popd 2>/dev/null
fi

# 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"
popd
rm -rf hashes "$nss_db"
rm -rf hashes

echo "Staging the following files for signing:"
cpio -t <"$OUTPUTDIR/hashes.cpio.rsasign"
Expand Down