diff --git a/package/pkg-download.mk b/package/pkg-download.mk index d3cd0c144e..7f208d538b 100644 --- a/package/pkg-download.mk +++ b/package/pkg-download.mk @@ -58,6 +58,17 @@ domainseparator=$(if $(1),$(1),/) # github(user,package,version): returns site of GitHub repository github = https://github.com/$(1)/$(2)/archive/$(3) +# Helper for checking a tarball's checksum +# If the hash does not match, remove the incorrect file +# $(1): the path to the file with the hashes +# $(2): the full path to the file to check +define VERIFY_HASH + if ! support/download/check-hash $(1) $(2); then \ + rm -f $(2); \ + exit 1; \ + fi +endef + ################################################################################ # The DOWNLOAD_* helpers are in charge of getting a working copy # of the source repository for their corresponding SCM, @@ -148,7 +159,8 @@ endef define DOWNLOAD_SCP test -e $(DL_DIR)/$(2) || \ $(EXTRA_ENV) support/download/scp '$(call stripurischeme,$(call qstrip,$(1)))' \ - $(DL_DIR)/$(2) + $(DL_DIR)/$(2) && \ + $(call VERIFY_HASH,$(PKGDIR)/$($(PKG)_NAME).hash,$(DL_DIR)/$(2)) endef define SOURCE_CHECK_SCP @@ -179,7 +191,8 @@ endef define DOWNLOAD_WGET test -e $(DL_DIR)/$(2) || \ - $(EXTRA_ENV) support/download/wget '$(call qstrip,$(1))' $(DL_DIR)/$(2) + $(EXTRA_ENV) support/download/wget '$(call qstrip,$(1))' $(DL_DIR)/$(2) && \ + $(call VERIFY_HASH,$(PKGDIR)/$($(PKG)_NAME).hash,$(DL_DIR)/$(2)) endef define SOURCE_CHECK_WGET @@ -193,7 +206,8 @@ endef define DOWNLOAD_LOCALFILES test -e $(DL_DIR)/$(2) || \ $(EXTRA_ENV) support/download/cp $(call stripurischeme,$(call qstrip,$(1))) \ - $(DL_DIR) + $(DL_DIR) && \ + $(call VERIFY_HASH,$(PKGDIR)/$($(PKG)_NAME).hash,$(DL_DIR)/$(2)) endef define SOURCE_CHECK_LOCALFILES diff --git a/support/download/check-hash b/support/download/check-hash new file mode 100755 index 0000000000..9ea7c415a1 --- /dev/null +++ b/support/download/check-hash @@ -0,0 +1,76 @@ +#!/bin/bash +set -e + +# Helper to check a file matches its known hash +# Call it with: +# $1: the full path to the file to check +# $2: the path of the file containing all the the expected hashes + +h_file="${1}" +file="${2}" + +# Does the hash-file exist? +if [ ! -f "${h_file}" ]; then + exit 0 +fi + +# Check one hash for a file +# $1: known hash +# $2: file (full path) +check_one_hash() { + _h="${1}" + _known="${2}" + _file="${3}" + + # Note: sha3 is not supported, since there is currently no implementation + # (the NIST has yet to publish the parameters). + case "${_h}" in + md5|sha1) ;; + sha224|sha256|sha384|sha512) ;; + *) # Unknown hash, exit with error + printf "ERROR: unknown hash '%s' for '%s'\n" \ + "${_h}" "${_file##*/}" >&2 + exit 1 + ;; + esac + + # Do the hashes match? + _hash=$( ${_h}sum "${_file}" |cut -d ' ' -f 1 ) + if [ "${_hash}" = "${_known}" ]; then + printf "%s: OK (%s: %s)\n" "${_file##*/}" "${_h}" "${_hash}" + return 0 + fi + + printf "ERROR: %s has wrong %s hash:\n" "${_file##*/}" "${_h}" >&2 + printf "ERROR: expected: %s\n" "${_known}" >&2 + printf "ERROR: got : %s\n" "${_hash}" >&2 + printf "ERROR: Incomplete download, or man-in-the-middle (MITM) attack\n" >&2 + + exit 1 +} + +# Do we know one or more hashes for that file? +nb_checks=0 +while read t h f; do + case "${t}" in + ''|'#'*) + # Skip comments and empty lines + continue + ;; + *) + if [ "${f}" = "${file##*/}" ]; then + check_one_hash "${t}" "${h}" "${file}" + : $((nb_checks++)) + fi + ;; + esac +done <"${h_file}" + +if [ ${nb_checks} -eq 0 ]; then + if [ -n "${BR2_ENFORCE_CHECK_HASH}" ]; then + printf "ERROR: No hash found for %s\n" "${file}" >&2 + exit 1 + else + printf "WARNING: No hash found for %s\n" "${file}" >&2 + fi +fi