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