#!/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}"