check-hash 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #!/usr/bin/env bash
  2. set -e
  3. # Helper to check a file matches its known hash
  4. # Call it with:
  5. # $1: the full path to the temporary file that was downloaded, and
  6. # that is to be checked
  7. # $2: the final basename of the file, to which it will be ultimately
  8. # saved as, to be able to match it to the corresponding hashes
  9. # in the .hash file
  10. # $*: the paths of the files containing all the expected hashes
  11. #
  12. # Exit codes:
  13. # 0: the hash file exists and the file to check matches all its hashes,
  14. # or the hash file does not exist
  15. # 1: unknown command-line option
  16. # 2: the hash file exists and the file to check does not match at least
  17. # one of its hashes
  18. # 3: the hash file exists and there was no hash to check the file against
  19. # 4: the hash file exists and at least one hash type is unknown
  20. while getopts :q OPT; do
  21. case "${OPT}" in
  22. q) exec >/dev/null;;
  23. \?) exit 1;;
  24. esac
  25. done
  26. shift $((OPTIND-1))
  27. file="${1}"
  28. base="${2}"
  29. shift 2
  30. declare -a h_files=( "${@}" )
  31. # Check one hash for a file
  32. # $1: algo hash
  33. # $2: known hash
  34. # $3: file (full path)
  35. # $4: hash file (full path)
  36. check_one_hash() {
  37. _h="${1}"
  38. _known="${2}"
  39. _file="${3}"
  40. _h_file="${4}"
  41. # Note: md5 is supported, but undocumented on purpose.
  42. # Note: sha3 is not supported, since there is currently no implementation
  43. # (the NIST has yet to publish the parameters).
  44. case "${_h}" in
  45. md5|sha1) ;;
  46. sha224|sha256|sha384|sha512) ;;
  47. *) # Unknown hash, exit with error
  48. printf "ERROR: unknown hash '%s' for '%s'\n" \
  49. "${_h}" "${base}" >&2
  50. exit 4
  51. ;;
  52. esac
  53. # Do the hashes match?
  54. _hash="$( "${_h}sum" "${_file}" |cut -d ' ' -f 1 )"
  55. if [ "${_hash}" = "${_known}" ]; then
  56. printf "%s: OK (%s: %s)\n" "${base}" "${_h}" "${_hash}"
  57. return 0
  58. fi
  59. printf "ERROR: while checking hashes from %s\n" "${_h_file}" >&2
  60. printf "ERROR: %s has wrong %s hash:\n" "${base}" "${_h}" >&2
  61. printf "ERROR: expected: %s\n" "${_known}" >&2
  62. printf "ERROR: got : %s\n" "${_hash}" >&2
  63. printf "ERROR: Incomplete download, or man-in-the-middle (MITM) attack\n" >&2
  64. exit 2
  65. }
  66. # Do we know one or more hashes for that file?
  67. nb_h_files=0
  68. nb_checks=0
  69. for h_file in "${h_files[@]}"; do
  70. [ -f "${h_file}" ] || continue
  71. : $((nb_h_files++))
  72. # shellcheck disable=SC2094 # we're really reading it only once
  73. while read -r t h f; do
  74. case "${t}" in
  75. ''|'#'*)
  76. # Skip comments and empty lines
  77. continue
  78. ;;
  79. *)
  80. if [ "${f}" = "${base}" ]; then
  81. # shellcheck disable=SC2094 # we're only printing the h_file filename
  82. check_one_hash "${t}" "${h}" "${file}" "${h_file}"
  83. : $((nb_checks++))
  84. fi
  85. ;;
  86. esac
  87. done <"${h_file}"
  88. done
  89. # shellcheck disable=SC2086 # nb_h_files is a non-empty int
  90. if [ ${nb_h_files} -eq 0 ]; then
  91. printf "WARNING: no hash file for %s\n" "${base}" >&2
  92. exit 0
  93. fi
  94. # shellcheck disable=SC2086 # nb_checks is a non-empty int
  95. if [ ${nb_checks} -eq 0 ]; then
  96. case " ${BR_NO_CHECK_HASH_FOR} " in
  97. *" ${base} "*)
  98. # File explicitly has no hash
  99. exit 0
  100. ;;
  101. esac
  102. printf "ERROR: No hash found for %s\n" "${base}" >&2
  103. exit 3
  104. fi