blob: 319cf01d44a179a15a488d069fa8f92f97d42c57 [file] [log] [blame]
#!/bin/bash
# get-verified-tarball
# --------------------
# Get Linux kernel tarball and cryptographically verify it,
# retrieving the PGP keys using the Web Key Directory (WKD)
# protocol if they are not already in the keyring.
#
# Pass the kernel version as the only parameter, or
# we'll grab the latest stable kernel.
#
# Example: ./get-verified-tarball 4.4.145
#
# Configurable parameters
# -----------------------
# Where to download the tarball and verification data.
TARGETDIR="$HOME/Downloads"
# If you set this to empty value, we'll make a temporary
# directory and fetch the verification keys from the
# Web Key Directory each time. Also, see the USEKEYRING=
# configuration option for an alternative that doesn't
# rely on WKD.
GNUPGHOME="$HOME/.gnupg"
# For CI and other automated infrastructure, you may want to
# create a keyring containing the keys belonging to:
# - autosigner@kernel.org
# - torvalds@kernel.org
# - gregkh@kernel.org
#
# To generate the keyring with these keys, do:
# gpg --export autosigner@ torvalds@ gregkh@ > keyring.gpg
# (or use full keyids for maximum certainty)
#
# Once you have keyring.gpg, install it on your CI system and set
# USEKEYRING to the full path to it. If unset, we generate our own
# from GNUPGHOME.
USEKEYRING=
# Point this at your GnuPG binary version 2.1.11 or above.
# If you are using USEKEYRING, GnuPG-1 will work, too.
GPGBIN="/usr/bin/gpg2"
GPGVBIN="/usr/bin/gpgv2"
# We need a compatible version of sha256sum, too
SHA256SUMBIN="/usr/bin/sha256sum"
# And curl
CURLBIN="/usr/bin/curl"
# And we need the xz binary
XZBIN="/usr/bin/xz"
# You shouldn't need to modify this, unless someone
# other than Linus or Greg start releasing kernels.
DEVKEYS="torvalds@kernel.org gregkh@kernel.org"
# Don't add this to DEVKEYS, as it plays a wholly
# different role and is NOT a key that should be used
# to verify kernel tarball signatures (just the checksums).
SHAKEYS="autosigner@kernel.org"
# What kernel version do you want?
VER=${1}
if [[ -z ${VER} ]]; then
# Assume you want the latest stable
VER=$(${CURLBIN} -sL https://www.kernel.org/finger_banner \
| grep 'latest stable version' \
| awk -F: '{gsub(/ /,"", $0); print $2}')
fi
if [[ -z ${VER} ]]; then
echo "Could not figure out the latest stable version."
exit 1
fi
MAJOR="$(echo ${VER} | cut -d. -f1)"
if [[ ${MAJOR} -lt 3 ]]; then
echo "This script only supports kernel v3.x.x and above"
exit 1
fi
if [[ ! -d ${TARGETDIR} ]]; then
echo "${TARGETDIR} does not exist"
exit 1
fi
TARGET="${TARGETDIR}/linux-${VER}.tar.xz"
# Do we already have this file?
if [[ -f ${TARGET} ]]; then
echo "File ${TARGETDIR}/linux-${VER}.tar.xz already exists."
exit 0
fi
# Start by making sure our GnuPG environment is sane
if [[ ! -x ${GPGBIN} ]]; then
echo "Could not find gpg in ${GPGBIN}"
exit 1
fi
if [[ ! -x ${GPGVBIN} ]]; then
echo "Could not find gpgv in ${GPGVBIN}"
exit 1
fi
# Let's make a safe temporary directory for intermediates
TMPDIR=$(mktemp -d ${TARGETDIR}/linux-tarball-verify.XXXXXXXXX.untrusted)
echo "Using TMPDIR=${TMPDIR}"
# Are we using a keyring?
if [[ -z ${USEKEYRING} ]]; then
if [[ -z ${GNUPGHOME} ]]; then
GNUPGHOME="${TMPDIR}/gnupg"
elif [[ ! -d ${GNUPGHOME} ]]; then
echo "GNUPGHOME directory ${GNUPGHOME} does not exist"
echo -n "Create it? [Y/n]"
read YN
if [[ ${YN} == 'n' ]]; then
echo "Exiting"
rm -rf ${TMPDIR}
exit 1
fi
fi
mkdir -p -m 0700 ${GNUPGHOME}
echo "Making sure we have all the necessary keys"
${GPGBIN} --batch --quiet \
--homedir ${GNUPGHOME} \
--auto-key-locate wkd \
--locate-keys ${DEVKEYS} ${SHAKEYS}
# If this returned non-0, we bail
if [[ $? != "0" ]]; then
echo "Something went wrong fetching keys"
rm -rf ${TMPDIR}
exit 1
fi
# Make a temporary keyring and set USEKEYRING to it
USEKEYRING=${TMPDIR}/keyring.gpg
${GPGBIN} --batch --export ${DEVKEYS} ${SHAKEYS} > ${USEKEYRING}
fi
# Now we make two keyrings -- one for the autosigner, and
# the other for kernel developers. We do this in order to
# make sure that we never verify kernel tarballs using the
# autosigner keys, only using developer keys.
SHAKEYRING=${TMPDIR}/shakeyring.gpg
${GPGBIN} --batch \
--no-default-keyring --keyring ${USEKEYRING} \
--export ${SHAKEYS} > ${SHAKEYRING}
DEVKEYRING=${TMPDIR}/devkeyring.gpg
${GPGBIN} --batch \
--no-default-keyring --keyring ${USEKEYRING} \
--export ${DEVKEYS} > ${DEVKEYRING}
# Now that we know we can verify them, grab the contents
TXZ="https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${VER}.tar.xz"
SIG="https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${VER}.tar.sign"
SHA="https://www.kernel.org/pub/linux/kernel/v${MAJOR}.x/sha256sums.asc"
# Before we verify the developer signature, we make sure that the
# tarball matches what is on the kernel.org master. This avoids
# CDN cache poisoning that could, in theory, use vulnerabilities in
# the XZ binary to alter the verification process or compromise the
# system performing the verification.
SHAFILE=${TMPDIR}/sha256sums.asc
echo "Downloading the checksums file for linux-${VER}"
if ! ${CURLBIN} -sL -o ${SHAFILE} ${SHA}; then
echo "Failed to download the checksums file"
rm -rf ${TMPDIR}
exit 1
fi
echo "Verifying the checksums file"
COUNT=$(${GPGVBIN} --keyring=${SHAKEYRING} --status-fd=1 ${SHAFILE} \
| grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)')
if [[ ${COUNT} -lt 2 ]]; then
echo "FAILED to verify the sha256sums.asc file."
rm -rf ${TMPDIR}
exit 1
fi
# Grab only the tarball we want from the full list
SHACHECK=${TMPDIR}/sha256sums.txt
grep "linux-${VER}.tar.xz" ${SHAFILE} > ${SHACHECK}
echo
echo "Downloading the signature file for linux-${VER}"
SIGFILE=${TMPDIR}/linux-${VER}.tar.asc
if ! ${CURLBIN} -sL -o ${SIGFILE} ${SIG}; then
echo "Failed to download the signature file"
rm -rf ${TMPDIR}
exit 1
fi
echo "Downloading the XZ tarball for linux-${VER}"
TXZFILE=${TMPDIR}/linux-${VER}.tar.xz
if ! ${CURLBIN} -L -o ${TXZFILE} ${TXZ}; then
echo "Failed to download the tarball"
rm -rf ${TMPDIR}
exit 1
fi
pushd ${TMPDIR} >/dev/null
echo "Verifying checksum on linux-${VER}.tar.xz"
if ! ${SHA256SUMBIN} -c ${SHACHECK}; then
echo "FAILED to verify the downloaded tarball checksum"
popd >/dev/null
rm -rf ${TMPDIR}
exit 1
fi
popd >/dev/null
echo
echo "Verifying developer signature on the tarball"
COUNT=$(${XZBIN} -cd ${TXZFILE} \
| ${GPGVBIN} --keyring=${DEVKEYRING} --status-fd=1 ${SIGFILE} - \
| grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)')
if [[ ${COUNT} -lt 2 ]]; then
echo "FAILED to verify the tarball!"
rm -rf ${TMPDIR}
exit 1
fi
mv -f ${TXZFILE} ${TARGET}
rm -rf ${TMPDIR}
echo
echo "Successfully downloaded and verified ${TARGET}"