BASH   19

verify digests sh

Guest on 31st July 2022 05:13:09 PM

  1. #!/bin/bash
  2. # Name: verify-digests.sh
  3. # Title: Gentoo Linux release digest verification
  4. # Author: Robin H Johnson <robbat2@gentoo.org>
  5. # Copyright  Gentoo Authors
  6. # Distributed under the terms of the GNU General Public License v2
  7. #
  8. # Description:
  9. # This script exists to help mirrors verify raw digests of release files, to
  10. # detect possible disk and filesystem corruptions.  By design, it does NOT check
  11. # GPG signatures.
  12. #
  13. # Usage:
  14. # verify-digests.sh [FILES-OR-DIRECTORIES...]
  15. #
  16. # If passed a digest file:
  17. # - it will be checked.
  18. # If passed a non-digest file:
  19. # - that immediate directory will be checked for all digest files.
  20. # If passed a directory:
  21. # - it and all subdirs will be checked for all digest files.
  22. # If passed no arguments:
  23. # - it will act like the directory '.' was passed.
  24. #
  25. # Return value:
  26. # On success, exits zero.
  27. # On failures, exits non-zero, and writes a file of errors to $TMPDIR.
  28.  
  29.  
  30. # Take Gentoo digest files and convert to a plain BSD-format digest file.
  31. # - strip any PGP signing
  32. # - pass existing BSD-format digest
  33. # - convert coreutils-format to BSD-format
  34. transform_digest() {
  35.         sed -n -r \
  36.                 -e '/BEGIN (PGP|GPG) SIGNED MESSAGE/,/^$/d' \
  37.                 -e '/BEGIN (PGP|GPG) SIGNATURE/,/END (PGP|GPG) SIGNATURE/{d}' \
  38.                 -e 'p' \
  39.         | \
  40.         awk \
  41.                 -e '/^# .* HASH$/{hash=$2}' \
  42.                 -e '/^[[:xdigit:]]+[[:space:]]+.+/{if(hash != ""){printf "%s (%s) = %s\n",hash,$2,$1}}' \
  43.                 -e '/^((SHA|MD|RIPEMD)[0-9]+|WHIRLPOOL) \(.*\) = [[:xdigit:]]+/{print $0}' \
  44.                 -e '/^((SHA|MD|RIPEMD)[0-9]+|WHIRLPOOL) [[:xdigit:]]+ [^[:space:]]+$/{ printf "%s (%s) = %s\n",$1,$3,$2; }'
  45. }
  46.  
  47. # Pass all directory arguments to find
  48. # Keep all file arguments as-is (so you can pass .asc files directly)
  49. DIGESTS_ARGS=( )
  50. DIGESTS_FIND=( )
  51. if [[ ${#@} -eq 0 ]]; then
  52.         DIGESTS_FIND+=( . )
  53. else
  54.         for f in "${@}" ; do
  55.                 if [ -d "$f" ]; then
  56.                         DIGESTS_FIND+=( "$f" )
  57.                 else
  58.                         DIGESTS_ARGS+=( "$f" )
  59.                 fi
  60.         done
  61. fi
  62.  
  63. # Check if non-dir arguments were digest files or files that you want to get checked
  64. DIGESTS_ARGS2=( )
  65. for f in "${DIGESTS_ARGS[@]}" ; do
  66.         if [[ "${f/DIGEST}" != "$f" ]] || grep -sq -m 1 -e '# MD5 HASH' -e '# SHA[0-9]\+ HASH' -e ') = [0-9a-f]\+' $f; then
  67.                 DIGESTS_ARGS2+=( "$f" )
  68.         else
  69.                 d=$( dirname "$f" )
  70.                 DIGESTS_FIND2=( )
  71.                 readarray -t DIGESTS_FIND2 <<< "$(find "$d" -maxdepth 1  ! -type d \( -name '*.DIGESTS' -o -name '*.DIGESTS.asc' \) | fmt -1 |sort | uniq)"
  72.                 DIGESTS_ARGS2+=( "${DIGESTS_FIND2[@]}" )
  73.                 DIGESTS_FIND2=( )
  74.         fi
  75. done
  76. if [[ "${#DIGESTS_FIND[@]}" -gt 0 ]]; then
  77.         readarray -t DIGESTS_FIND <<< "$(find "${DIGESTS_FIND[@]}" ! -type d \( -name '*.DIGESTS' -o -name '*.DIGESTS.asc' \) | fmt -1 | sort | uniq )"
  78. fi
  79. # merge all items
  80. DIGESTS=( "${DIGESTS_ARGS2[@]}" "${DIGESTS_FIND[@]}" )
  81.  
  82.  
  83. # Prefer signed digests where possible, but sometimes they were in the original
  84. # .DIGESTS file, and other times there was a seperate .asc file.
  85. DIGESTS2="$(echo "${DIGESTS[@]}" | fmt -1 |sed '/.asc$/s/.asc$//' | sort | uniq)"
  86. DIGESTS=( )
  87. for d in ${DIGESTS2} ; do
  88.         if [ -e "${d}" -a -e "${d}.asc" ]; then
  89.                 DIGESTS+=( "${d}.asc" )
  90.         elif [ ! -e "${d}" -a -e "${d}.asc" ]; then
  91.                 DIGESTS+=( "${d}.asc" )
  92.         elif [ -e "${d}" -a ! -e "${d}.asc" ]; then
  93.                 DIGESTS+=( "${d}" )
  94.         fi
  95. done
  96.  
  97.  
  98. # Setup storage for digest conversion & results
  99. T=$(date -u +%Y%m%dT%H%M%SZ)
  100. tmp1=$(mktemp --tmpdir)
  101. tmp2=$(mktemp --tmpdir)
  102. failures=$(mktemp --tmpdir gentoo-failures.$T.XXXXXXXXXX)
  103. trap "rm -f $tmp1 $tmp2" SIGINT SIGTERM
  104.  
  105. # Now check them
  106. failed_digests=()
  107. for d in $(echo "${DIGESTS[@]}" | fmt -1 | sort | uniq); do
  108.   sleep 0.01
  109.   echo -n "Checking digests from $d: "
  110.   transform_digest < "$d" >"$tmp1"
  111.   # add leading & trailing space to match
  112.   hashes=" $(awk '{print $1}' "$tmp1" | sort | uniq ) "
  113.   checked=0
  114.   found=0
  115.   # order by strength
  116.   for h in SHA512 SHA384 SHA256 SHA224 SHA1 MD5 ; do
  117.           sleep 0.01
  118.           [[ $found -eq 1 ]] && break
  119.           if [[ "${hashes/$h}" != "${hashes}" ]]; then
  120.                   found=1
  121.                   echo "using $h"
  122.                   pushd $(dirname $d) >/dev/null
  123.                   cmd=$(echo ${h}sum | tr '[:upper:]' '[:lower:]')
  124.                   grep "^$h " $tmp1 | ionice -c 3 --ignore ${cmd} -c - | tee "$tmp2"
  125.                   rc=${PIPESTATUS[1]}
  126.                   if [ $rc -ne 0 ]; then
  127.                           failed_digests+=("$d")
  128.                           cat "$tmp2" >> "$failures"
  129.                   fi
  130.                   checked=1
  131.                   popd >/dev/null
  132.           fi
  133.   done
  134.   if [[ $checked -eq 0 ]]; then
  135.           echo " FAIL - no usable digest"
  136.   fi
  137. done
  138.  
  139. # Handle output of errors
  140. if [[ "${#failed_digests[@]}" -eq 0 ]]; then
  141.         exit 0
  142. else
  143.         echo "----"
  144.         echo "Failures detected in the following DIGESTS:" 1>&2
  145.         for f in "${failed_digests[@]}"; do
  146.                 echo "$f" 1>&2
  147.         done
  148.         echo "----" 1>&2
  149.         echo "Complete output of failed DIGESTS, stored in $failures:" 1>&2
  150.         cat "$failures" 1>&2
  151.         exit 1
  152. fi

Raw Paste


Login or Register to edit or fork this paste. It's free.