|
|
@@ -0,0 +1,346 @@ |
|
|
|
#!/bin/sh - |
|
|
|
|
|
|
|
STOREDIR="$HOME/.snapaid" |
|
|
|
|
|
|
|
setdefaults() { |
|
|
|
GPG=$(which gpg2) |
|
|
|
WGET=$(which wget) |
|
|
|
SHASUM=$(which shasum) |
|
|
|
} |
|
|
|
setdefaults |
|
|
|
|
|
|
|
if [ ! -x "$GPG" ]; then |
|
|
|
echo 'Failed to find gpg2 executable' |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
|
|
|
|
if [ ! -x "$WGET" ]; then |
|
|
|
echo 'Failed to find wget executable' |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
|
|
|
|
if [ ! -x "$SHASUM" ]; then |
|
|
|
echo 'Failed to find shasum executable' |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
|
|
|
|
#wget: |
|
|
|
# -N for timestamps |
|
|
|
# --backups=x for backing up |
|
|
|
|
|
|
|
completeurl="https://www.funkthat.com/~jmg/FreeBSD-snap/snapshot.complete.idx.xz" |
|
|
|
currenturl="https://www.funkthat.com/~jmg/FreeBSD-snap/snapshot.idx.xz" |
|
|
|
|
|
|
|
# type release arch platform date svnrev xxx fname url mid |
|
|
|
# 1 2 3 4 5 6 7 8 9 10 |
|
|
|
# iso 11.1-STABLE arm-armv6 BEAGLEBONE 20180315 r330998 xxx FreeBSD-11.1-STABLE-arm-armv6-BEAGLEBONE-20180315-r330998.img.xz https://download.freebsd.org/ftp/snapshots/ISO-IMAGES/11.1/FreeBSD-11.1-STABLE-arm-armv6-BEAGLEBONE-20180315-r330998.img.xz 20180316000842.GA7399@FreeBSD.org |
|
|
|
|
|
|
|
set -e |
|
|
|
|
|
|
|
# This is used for some testing functions |
|
|
|
copy_function() { |
|
|
|
declare -F "$1" > /dev/null || return 1 |
|
|
|
local func="$(declare -f "$1")" |
|
|
|
eval "${2}(${func#*\(}" |
|
|
|
} |
|
|
|
|
|
|
|
# Test function to cause a bad input |
|
|
|
cmd_failure() { |
|
|
|
exit 1 |
|
|
|
} |
|
|
|
|
|
|
|
# First time fails, second time run real command |
|
|
|
gpg_first_fails() { |
|
|
|
copy_function verifygpg_orig verifygpg |
|
|
|
return 1 |
|
|
|
} |
|
|
|
|
|
|
|
# Make sure that the storage directory is present |
|
|
|
mkstore() { |
|
|
|
mkdir "$STOREDIR" 2>/dev/null || : |
|
|
|
} |
|
|
|
|
|
|
|
# Given a message id, get the raw body and store it. |
|
|
|
get_raw() { |
|
|
|
mkstore |
|
|
|
|
|
|
|
mid="$1" |
|
|
|
|
|
|
|
midfile="$STOREDIR/$mid".raw |
|
|
|
|
|
|
|
if [ ! -e "$midfile" ]; then |
|
|
|
# get the location, it's a database lookup |
|
|
|
loc=$($WGET --max-redirect=0 --method=HEAD -S -o - -O - 'https://docs.freebsd.org/cgi/mid.cgi?'"$mid" 2>/dev/null | awk 'tolower($1) == "location:" { print $2; exit }') |
|
|
|
|
|
|
|
# if it's relative, add https |
|
|
|
if [ x"$loc" != x"${loc#//}" ]; then |
|
|
|
# add https |
|
|
|
loc="https:$loc" |
|
|
|
fi |
|
|
|
|
|
|
|
# get the raw part |
|
|
|
tmpfile="$STOREDIR/.tmp.$$.$mid".raw |
|
|
|
|
|
|
|
# strip out everything but message id and first signed part |
|
|
|
$WGET -O - "$loc"+raw 2>/dev/null | awk ' |
|
|
|
tolower($1) == "message-id:" && check == 0 { |
|
|
|
print |
|
|
|
} |
|
|
|
|
|
|
|
$0 == "-----BEGIN PGP SIGNED MESSAGE-----" { |
|
|
|
sigbody = 1 |
|
|
|
} |
|
|
|
|
|
|
|
sigbody { |
|
|
|
print |
|
|
|
} |
|
|
|
|
|
|
|
$0 == "-----END PGP SIGNATURE-----" { |
|
|
|
sigbody = 0 |
|
|
|
}' > "$tmpfile" |
|
|
|
|
|
|
|
if verifygpg "$tmpfile"; then |
|
|
|
mv "$tmpfile" "$STOREDIR/$mid.raw" |
|
|
|
else |
|
|
|
rm "$tmpfile" |
|
|
|
echo Bad signature from mail archive. |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
else |
|
|
|
if ! verifygpg "$midfile"; then |
|
|
|
rm "$midfile" |
|
|
|
get_raw "$mid" |
|
|
|
return $? |
|
|
|
fi |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
fetch() { |
|
|
|
mkstore |
|
|
|
|
|
|
|
(cd "$STOREDIR" && $WGET -N "$1" >/dev/null 2>&1) |
|
|
|
} |
|
|
|
|
|
|
|
getvermid() { |
|
|
|
xzcat "$STOREDIR"/snapshot.complete.idx.xz | awk '$8 == fname { |
|
|
|
print $10 |
|
|
|
}' fname="$i" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
# takes basename of arg, which much exist in STOREDIR, and verifies |
|
|
|
# that the signature is valid. |
|
|
|
verifygpg() { |
|
|
|
local fname |
|
|
|
fname=$(basename "$1") |
|
|
|
if ! (cd "$STOREDIR" && $GPG --verify "$fname" 2> /dev/null); then |
|
|
|
echo 'ERROR: PGP signature verification failed!' |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
# Verifies the file |
|
|
|
verifyfile() { |
|
|
|
local fname |
|
|
|
local hashinfo |
|
|
|
local algo hash |
|
|
|
|
|
|
|
fname="$STOREDIR/${1}.raw" |
|
|
|
hashinfo=$(awk ' |
|
|
|
check && $2 == "('"$2"')" { |
|
|
|
hashes[$1] = $4 |
|
|
|
} |
|
|
|
|
|
|
|
$0 == "-----BEGIN PGP SIGNED MESSAGE-----" { |
|
|
|
check = 1 |
|
|
|
} |
|
|
|
|
|
|
|
$0 == "-----BEGIN PGP SIGNATURE-----" { |
|
|
|
check = 0 |
|
|
|
} |
|
|
|
|
|
|
|
END { |
|
|
|
if ("SHA512" in hashes) |
|
|
|
algo = "SHA512" |
|
|
|
else if ("SHA256" in hashes) |
|
|
|
algo = "SHA256" |
|
|
|
else { |
|
|
|
print "unkn BADHASH" |
|
|
|
exit 1 |
|
|
|
} |
|
|
|
|
|
|
|
print algo " " hashes[algo] |
|
|
|
} |
|
|
|
' "$fname") |
|
|
|
read algo hash <<-EOF |
|
|
|
${hashinfo} |
|
|
|
EOF |
|
|
|
|
|
|
|
if [ x"$algo" == x"unkn" -o x"$algo" = x"" ]; then |
|
|
|
echo 'Unable to find hash for file.' |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
|
|
|
|
echo "$hash $2" | $SHASUM -a "${algo#SHA}" -c - |
|
|
|
} |
|
|
|
|
|
|
|
if [ x"$1" = x"verify" ]; then |
|
|
|
shift |
|
|
|
|
|
|
|
fetch "$completeurl" |
|
|
|
|
|
|
|
for i in "$@"; do |
|
|
|
vermid=$(getvermid "$i") |
|
|
|
if [ x"$vermid" = x"" ]; then |
|
|
|
echo "Unable to find entry for: $i" |
|
|
|
continue |
|
|
|
fi |
|
|
|
|
|
|
|
get_raw "$vermid" |
|
|
|
if ! verifygpg "$vermid".raw; then |
|
|
|
echo "Unable to verify: $i" |
|
|
|
fi |
|
|
|
|
|
|
|
verifyfile "$verurl" "$i" |
|
|
|
done |
|
|
|
elif [ x"$1" = x"find" ]; then |
|
|
|
fetch "$currenturl" |
|
|
|
|
|
|
|
tmpdir=$(mktemp -d -t snapaid) |
|
|
|
|
|
|
|
trap "rm -rf $tmpdir" 0 |
|
|
|
|
|
|
|
( cd "$tmpdir"; |
|
|
|
xzcat "$STOREDIR"/snapshot.idx.xz | sort -r -k 5 > selection; |
|
|
|
while :; do |
|
|
|
# display current list |
|
|
|
cnt=$(wc -l < selection) |
|
|
|
awk ' |
|
|
|
BEGIN { |
|
|
|
fmtstr = "%2s %-3s %-15s %-18s %-18s %-8s %-7s\n" |
|
|
|
printf(fmtstr, "#", "TYP", "RELEASE", "ARCH", "PLATFORM/TYPE", "DATE", "SVNREV") |
|
|
|
cnt = 1 |
|
|
|
} |
|
|
|
|
|
|
|
{ |
|
|
|
if ($4 == "xxx") |
|
|
|
plt=$7 |
|
|
|
else |
|
|
|
plt=$4 |
|
|
|
printf(fmtstr, cnt, $1, $2, $3, plt, $5, $6) |
|
|
|
if (cnt >= 20) |
|
|
|
exit 0 |
|
|
|
|
|
|
|
cnt += 1 |
|
|
|
} |
|
|
|
' selection |
|
|
|
|
|
|
|
read -p 'Select image, enter search term, reset, or quit: ' sel |
|
|
|
if [ x"$sel" = x"reset" ]; then |
|
|
|
xzcat "$STOREDIR"/snapshot.idx.xz | sort -r -k 5 > selection; |
|
|
|
continue |
|
|
|
elif [ x"$sel" = x"quit" ]; then |
|
|
|
echo "$sel" > sel |
|
|
|
break |
|
|
|
fi |
|
|
|
|
|
|
|
if [ "$cnt" -gt 20 ]; then |
|
|
|
cnt=20 |
|
|
|
fi |
|
|
|
|
|
|
|
if [ "$sel" -ge 1 -a "$sel" -le "$cnt" ] 2>/dev/null; then |
|
|
|
echo selected image $sel |
|
|
|
echo $(tail -n +"$sel" selection | head -n 1) > sel |
|
|
|
break |
|
|
|
else |
|
|
|
# restrict |
|
|
|
|
|
|
|
if ! grep -- "$sel" selection > selection.new; then |
|
|
|
echo WARNING: Ignoring selection, no results. |
|
|
|
else |
|
|
|
mv selection.new selection |
|
|
|
fi |
|
|
|
fi |
|
|
|
done |
|
|
|
) |
|
|
|
|
|
|
|
sel=$(cat "$tmpdir"/sel) |
|
|
|
if [ x"$sel" = x"quit" ]; then |
|
|
|
exit 0 |
|
|
|
fi |
|
|
|
|
|
|
|
echo $sel |
|
|
|
fname=$(cut -f 8 -d ' ' "$tmpdir"/sel) |
|
|
|
dlurl=$(cut -f 9 -d ' ' "$tmpdir"/sel) |
|
|
|
verurl=$(cut -f 10 -d ' ' "$tmpdir"/sel) |
|
|
|
|
|
|
|
# fetch link |
|
|
|
$WGET -- "$dlurl" |
|
|
|
|
|
|
|
# verify image |
|
|
|
fetch "$verurl" |
|
|
|
if ! verifygpg "$verurl"; then |
|
|
|
echo "Unable to verify: $fname" |
|
|
|
fi |
|
|
|
|
|
|
|
if ! verifyfile "$verurl" "$fname"; then |
|
|
|
rm "$fname" |
|
|
|
fi |
|
|
|
elif [ x"$1" = x"test" ]; then |
|
|
|
# Run various tests |
|
|
|
|
|
|
|
# Test getting the raw file |
|
|
|
echo 'Testing get_raw success...' |
|
|
|
mid='20160122055622.GA87581@FreeBSD.org' |
|
|
|
get_raw "$mid" |
|
|
|
|
|
|
|
# Verify resulsts |
|
|
|
(cd "$STOREDIR" && echo '6e53df5995b6cc423c7f2d63b6df52d5d7f70e8586c25f91433fd8a1a2466e77be6a38884bde8bedd9ff6e7deb0215a66e1c2a16e4955503c20445e649a5fb47 20160122055622.GA87581@FreeBSD.org.raw' | $SHASUM -a 512 -c) |
|
|
|
echo passed |
|
|
|
|
|
|
|
# If the file already exists, but fails verification, that |
|
|
|
# it will refetch and be correct |
|
|
|
echo 'Testing get_raw with file already present that fails verification...' |
|
|
|
copy_function verifygpg verifygpg_orig |
|
|
|
copy_function gpg_first_fails verifygpg |
|
|
|
get_raw "$mid" |
|
|
|
|
|
|
|
(cd "$STOREDIR" && echo '6e53df5995b6cc423c7f2d63b6df52d5d7f70e8586c25f91433fd8a1a2466e77be6a38884bde8bedd9ff6e7deb0215a66e1c2a16e4955503c20445e649a5fb47 20160122055622.GA87581@FreeBSD.org.raw' | $SHASUM -a 512 -c) |
|
|
|
|
|
|
|
echo passed |
|
|
|
|
|
|
|
# If the file already exists, a "broken" wget won't cause |
|
|
|
# a problem |
|
|
|
echo 'Testing get_raw with file already present...' |
|
|
|
WGET=cmd_failure |
|
|
|
get_raw "$mid" |
|
|
|
|
|
|
|
echo passed |
|
|
|
|
|
|
|
# Test failure |
|
|
|
echo 'Testing get_raw fails w/ bad data...' |
|
|
|
WGET=cmd_failure |
|
|
|
rm "$STOREDIR/$mid.raw" |
|
|
|
|
|
|
|
# it should fail |
|
|
|
! get_raw "$mid" |
|
|
|
|
|
|
|
# and the desired file should not exist |
|
|
|
if [ -e "$STOREDIR/$mid.raw" ]; then |
|
|
|
echo 'Test failed!' |
|
|
|
exit 1; |
|
|
|
fi |
|
|
|
echo passed |
|
|
|
|
|
|
|
setdefaults |
|
|
|
else |
|
|
|
echo "Unknown verb: $1" |
|
|
|
echo "Usage:" |
|
|
|
echo " $0 verify file ..." |
|
|
|
echo " $0 find" |
|
|
|
echo "" |
|
|
|
echo "The verify option will attempt to verify each file specified." |
|
|
|
echo "" |
|
|
|
echo "The find option will start up an interactive session to find" |
|
|
|
echo "and select the snapshot to download and verify." |
|
|
|
fi |