Skip to content
Merged
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
96 changes: 94 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ Usage() {
echo -e "\t\t-i --install-dep (optional) Attempt to install dependencies."
echo -e "\t\t-v --verbose (optional) Show make output on screen for filesystem builds as well as write it to the log file."
echo -e "\t\t --fs-download-only (optional) Only download Buildroot source packages for each filesystem."
echo -e "\t\t --sign-key (optional) Private key used to sign the kernel for UEFI Secure Boot."
echo -e "\t\t --sign-cert (optional) Certificate matching --sign-key, PEM or DER."
echo -e "\t\t Both are required together."
echo -e "\t\t Can also be given as \$FOS_SIGN_KEY / \$FOS_SIGN_CERT."
echo -e "\t\t-h --help -? Display this message."
exit 0
}
[[ -n "$arch" ]] && unset "$arch"

shortopts="?hkfnia:p:v"
longopts="help,kernel-only,filesystem-only,noconfirm,install-dep,arch:,path:,verbose,fs-download-only"
longopts="help,kernel-only,filesystem-only,noconfirm,install-dep,arch:,path:,verbose,fs-download-only,sign-key:,sign-cert:"

optargs=$(getopt -o "$shortopts" -l "$longopts" -n "$0" -- "$@")
[[ $? -ne 0 ]] && Usage
Expand Down Expand Up @@ -76,6 +80,14 @@ while :; do
buildPath=$2
shift 2
;;
--sign-key)
signKey=$2
shift 2
;;
--sign-cert)
signCert=$2
shift 2
;;
--)
shift
break
Expand All @@ -94,6 +106,54 @@ done
[[ -z $installDep ]] && installDep="n"
[[ -z $verbose ]] && verbose="n"
[[ -z $fsDownloadOnly ]] && fsDownloadOnly="n"
[[ -z $signKey ]] && signKey="$FOS_SIGN_KEY"
[[ -z $signCert ]] && signCert="$FOS_SIGN_CERT"

# Signing is entirely opt-in: with neither set, every artifact is produced
# exactly as it always was. Half a pair is always a mistake, so refuse it rather
# than silently shipping an unsigned kernel someone believes is signed.
if [[ -n $signKey || -n $signCert ]]; then
if [[ -z $signKey || -z $signCert ]]; then
echo "Error: --sign-key and --sign-cert must be given together."
Usage
fi
for f in "$signKey" "$signCert"; do
if [[ ! -r $f ]]; then
echo "Error: cannot read signing file '$f'."
exit 1
fi
done
if ! command -v sbsign >/dev/null 2>&1; then
echo "Error: sbsign not found. Install sbsigntool (Debian/Ubuntu) or sbsigntools (RHEL/Fedora)."
exit 1
fi
# sbsign reads certificates with OpenSSL's PEM_read_bio_X509 and rejects
# DER outright, while mokutil and MokManager -- the tools that enrol the
# same certificate on a client -- want DER. Anyone following the Secure
# Boot how-to therefore ends up holding one of each, with nothing telling
# them which tool takes which, and handing over the wrong one produces:
#
# Can't load certificate from file 'MOK.der'
# error:0480006C:PEM routines:get_name:no start line
#
# which never mentions the format. Accept either and convert here, so the
# flag behaves the way --secure-boot-cert does in the installer.
if openssl x509 -in "$signCert" -inform pem -noout >/dev/null 2>&1; then
signCertPem="$signCert"
else
signCertPem=$(mktemp) || { echo "Error: could not create a temporary file."; exit 1; }
# The certificate is public, so a world-readable temp file leaks
# nothing -- it is the private key beside it that matters. Removed on
# exit regardless of how the build ends.
trap 'rm -f "$signCertPem"' EXIT
if ! openssl x509 -in "$signCert" -inform der -outform pem \
-out "$signCertPem" 2>/dev/null; then
echo "Error: '$signCert' is not a readable certificate (tried PEM and DER)."
exit 1
fi
echo " * Converted DER certificate '$signCert' to PEM for signing."
fi
fi

checkDependencies
installDependencies "$installDep"
Expand Down Expand Up @@ -230,6 +290,38 @@ function buildFilesystem() {
cd ..
}

# Sign a built kernel in place for UEFI Secure Boot. No-op unless --sign-key and
# --sign-cert were given. Must run before the artifact is checksummed so the
# published sha256 covers the signed image, not the one we threw away.
#
# sbsign will not cleanly re-sign an already-signed image, so it writes to a
# temp file and replaces the original only on success. A signing failure is
# fatal: a build that quietly emits an unsigned kernel is worse than no build,
# because it fails later at the client with a Security Policy Violation.
function signKernel() {
local kernelfile="$1"
local sberr
[[ -z $signKey ]] && return 0
dots "Signing $kernelfile for Secure Boot"
# $signCertPem, not $signCert: the latter may be the DER copy the admin
# enrols with, which sbsign cannot read. See the conversion above.
#
# sbsign's stderr is captured rather than discarded. It was going to
# /dev/null, which meant the one line explaining WHY signing failed --
# unreadable key, wrong passphrase, malformed image -- was thrown away and
# the operator got only "could not sign".
if ! sberr=$(sbsign --key "$signKey" --cert "$signCertPem" \
--output "${kernelfile}.signed" "$kernelfile" 2>&1 >/dev/null); then
echo "Failed"
echo " * sbsign could not sign $kernelfile"
[[ -n $sberr ]] && sed 's/^/ /' <<<"$sberr"
rm -f "${kernelfile}.signed"
exit 1
fi
mv -f "${kernelfile}.signed" "$kernelfile"
echo "Done"
}

function buildKernel() {
local arch="$1"
local kflags ktarget
Expand Down Expand Up @@ -334,7 +426,7 @@ function buildKernel() {
kernelfile='arm_Image'
;;
esac
[[ ! -f $compiledfile ]] && echo 'File not found.' || cp "$compiledfile" "$kernelfile" && sha256sum "$kernelfile" > "${kernelfile}.sha256"
[[ ! -f $compiledfile ]] && echo 'File not found.' || { cp "$compiledfile" "$kernelfile" && signKernel "$kernelfile" && sha256sum "$kernelfile" > "${kernelfile}.sha256"; }
cd ..
}

Expand Down