VerityBook/update.sh

322 lines
8.9 KiB
Bash
Raw Normal View History

2018-09-06 15:56:53 +02:00
#!/bin/bash -ex
2018-09-11 11:37:47 +02:00
CURDIR=$(pwd)
PROGNAME=${0##*/}
usage() {
cat << EOF
Usage: $PROGNAME [OPTION]
-h, --help Display this help
--force Update, even if the signature checks fail
2018-11-15 16:47:59 +01:00
--json JSON Update from JSON, instead of downloading
2018-09-11 11:37:47 +02:00
EOF
}
TEMP=$(
getopt -o '' \
2018-11-15 16:47:59 +01:00
--long json: \
2018-09-11 11:37:47 +02:00
--long force \
2018-10-23 16:13:51 +02:00
--long help \
2018-09-11 11:37:47 +02:00
-- "$@"
)
if (( $? != 0 )); then
usage >&2
exit 1
fi
eval set -- "$TEMP"
unset TEMP
while true; do
case "$1" in
2018-11-15 16:47:59 +01:00
'--json')
USE_JSON="$(readlink -e $2)"
2018-09-11 11:37:47 +02:00
shift 2; continue
;;
'--force')
FORCE="y"
2018-09-11 11:37:47 +02:00
shift 1; continue
;;
'--help')
usage
exit 0
2018-09-11 11:37:47 +02:00
;;
'--')
shift
break
;;
*)
echo 'Internal error!' >&2
exit 1
;;
esac
done
2018-09-06 15:56:53 +02:00
BASEURL="$1"
. /etc/os-release
CURRENT_ROOT_HASH=$(</proc/cmdline)
CURRENT_ROOT_HASH=${CURRENT_ROOT_HASH#*roothash=}
CURRENT_ROOT_HASH=${CURRENT_ROOT_HASH%% *}
2018-11-15 16:47:59 +01:00
CURRENT_IMAGE_SIZE=$(</proc/cmdline)
CURRENT_IMAGE_SIZE=${CURRENT_IMAGE_SIZE#*verity.imagesize=}
CURRENT_IMAGE_SIZE=${CURRENT_IMAGE_SIZE%% *}
2018-09-06 15:56:53 +02:00
CURRENT_ROOT_UUID=${CURRENT_ROOT_HASH:32:8}-${CURRENT_ROOT_HASH:40:4}-${CURRENT_ROOT_HASH:44:4}-${CURRENT_ROOT_HASH:48:4}-${CURRENT_ROOT_HASH:52:12}
bootdisk() {
UUID=$({ read -r -n 1 -d '' _; read -n 72 uuid; echo -n ${uuid,,}; } < /sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f)
2018-09-06 15:56:53 +02:00
[[ $UUID ]] || return 1
echo "/dev/disk/by-partuuid/$UUID"
return 0
}
get_disk() {
for dev in /dev/disk/by-path/*; do
[[ $dev -ef $1 ]] || continue
echo ${dev%-part*}
return 0
done
return 1
}
ROOT_DEV=$(get_disk $(bootdisk))
2018-09-06 15:56:53 +02:00
if ! [[ $ROOT_DEV ]]; then
2018-09-06 15:56:53 +02:00
echo "Current partitions booted from not found!"
exit 1
fi
unset FOUND
for dev in ${ROOT_DEV}-part*; do
[[ $(blkid -o value -s PARTLABEL $dev) == root1 ]] &&
ROOT1_DEV=$dev
[[ $(blkid -o value -s PARTLABEL $dev) == root2 ]] &&
ROOT2_DEV=$dev
[[ $(blkid -o value -s PARTUUID $dev) == $CURRENT_ROOT_UUID ]] &&
CURRENT_ROOT_DEV=$dev
[[ $ROOT1_DEV ]] && [[ $ROOT2_DEV ]] && [[ $CURRENT_ROOT_DEV ]] && break
2018-09-06 15:56:53 +02:00
done
if ! [[ $ROOT1_DEV ]] || ! [[ $ROOT2_DEV ]] || ! [[ $CURRENT_ROOT_DEV ]]; then
echo "Couldn't find partition numbers"
exit 1
fi
[[ $CURRENT_ROOT_DEV == $ROOT2_DEV ]] \
&& NEW_ROOT_NUM=1 && OLD_ROOT_NUM=2 \
&& NEW_ROOT_PARTNO=${ROOT1_DEV##*-part}
[[ $CURRENT_ROOT_DEV == $ROOT1_DEV ]] \
&& NEW_ROOT_NUM=2 && OLD_ROOT_NUM=1 \
&& NEW_ROOT_PARTNO=${ROOT2_DEV##*-part}
ROOT_PARTNO=${CURRENT_ROOT_DEV##*-part}
if ! [[ $NEW_ROOT_PARTNO ]] || ! [[ $ROOT_PARTNO ]] || ! [[ $ROOT_DEV ]]; then
2018-09-06 15:56:53 +02:00
echo "Couldn't find partition numbers"
exit 1
fi
[[ ${NAME} ]]
mkdir -p /var/cache/${NAME}
readonly MY_TMPDIR="$(mktemp -p "/var/cache/${NAME}/" -d)"
[ -d "$MY_TMPDIR" ] || {
printf "%s\n" "${PROGNAME}: mktemp -p '/var/cache/${NAME}/' -d failed." >&2
exit 1
}
# clean up after ourselves no matter how we die.
trap '
ret=$?;
[[ $MY_TMPDIR ]] && rm -rf --one-file-system -- "$MY_TMPDIR"
exit $ret;
' EXIT
# clean up after ourselves no matter how we die.
trap 'exit 1;' SIGINT
2018-11-15 16:47:59 +01:00
download_latest_json() {
JSON="${NAME}-latest.json"
rm -f "/var/cache/${NAME}/${JSON}"
curl "${BASEURL}/${JSON}" --output /var/cache/${NAME}/${JSON}
ROOT_HASH="$(jq -r '.roothash' /var/cache/${NAME}/${JSON})"
VERSION="$(jq -r '.version' /var/cache/${NAME}/${JSON})"
mv "/var/cache/${NAME}/${JSON}" "/var/cache/${NAME}/${NAME}-${VERSION}.json"
JSON="/var/cache/${NAME}/${NAME}-${VERSION}.json"
curl "${BASEURL}/${NAME}-${VERSION}.json.sig" \
--output /var/cache/${NAME}/${NAME}-${VERSION}.json.sig
if ! openssl dgst -sha256 -verify /etc/pki/${NAME}/pubkey \
-signature /var/cache/${NAME}/${NAME}-${VERSION}.json.sig \
"/var/cache/${NAME}/${NAME}-${VERSION}.json"
then
rm -f "/var/cache/${NAME}/${NAME}-${VERSION}.json" \
"/var/cache/${NAME}/${NAME}-${VERSION}.json.sig"
return 1
fi
return 0
}
2018-09-06 15:56:53 +02:00
2018-11-15 16:47:59 +01:00
if [[ $USE_JSON ]]; then
JSON="${USE_JSON}"
2018-09-11 11:37:47 +02:00
else
2018-11-15 16:47:59 +01:00
download_latest_json
fi
2018-10-23 16:13:51 +02:00
2018-11-15 16:47:59 +01:00
cd $MY_TMPDIR
2018-09-06 15:56:53 +02:00
2018-11-15 16:47:59 +01:00
JSONDIR="${JSON%/*}"
ROOT_HASH="$(jq -r '.roothash' ${JSON})"
IMAGE_SIZE="$(jq -r '.imagesize' ${JSON})"
if ! [[ $FORCE ]] && ( \
[[ $CURRENT_ROOT_HASH == $ROOT_HASH ]] \
|| [[ -f /efi/EFI/${NAME}/bootx64-XXX-$ROOT_HASH.efi ]]
)
then
echo "Already up2date"
exit 0
fi
check_delta_size() {
local HASH="$1"
local TARGET_HASH="$2"
local SIZE NEW_HASH JSON NEW_SIZE
curl -s "${BASEURL}/${NAME}-${HASH}-delta.json" \
--output /var/cache/${NAME}/${NAME}-${HASH}-delta.json \
|| return -1
curl -s "${BASEURL}/${NAME}-${HASH}-delta.json.sig" \
--output /var/cache/${NAME}/${NAME}-${HASH}-delta.json.sig \
|| return -1
openssl dgst -sha256 -verify /etc/pki/${NAME}/pubkey \
-signature /var/cache/${NAME}/${NAME}-${HASH}-delta.json.sig \
/var/cache/${NAME}/${NAME}-${HASH}-delta.json \
&>/dev/null || return -1
JSON="/var/cache/${NAME}/${NAME}-${HASH}-delta.json"
SIZE="$(jq -r '.deltasize' $JSON)"
NEW_HASH="$(jq -r '.roothash' ${JSON})"
if [[ $NEW_HASH != $TARGET_HASH ]]; then
NEW_SIZE=$(check_delta_size "$NEW_HASH" "$TARGET_HASH")
[[ $? == -1 ]] && return -1
SIZE=$(($SIZE + $NEW_SIZE))
2018-09-11 11:37:47 +02:00
fi
2018-11-15 16:47:59 +01:00
echo $SIZE
return 0
}
2018-10-23 16:13:51 +02:00
2018-11-15 16:47:59 +01:00
download_delta_images() {
local HASH="$1"
local TARGET_HASH="$2"
local SIZE NEW_HASH NEW_SIZE
local JSON="/var/cache/${NAME}/${NAME}-${HASH}-delta.json"
curl -s "${BASEURL}/${NAME}-${HASH}-delta.img" \
--output /var/cache/${NAME}/${NAME}-${HASH}-delta.img \
|| return -1
jq -r '.deltasig' ${JSON} | xxd -r -p > "$MY_TMPDIR/deltasig"
openssl dgst -sha256 -verify /etc/pki/${NAME}/pubkey \
-signature "$MY_TMPDIR/deltasig" \
/var/cache/${NAME}/${NAME}-${HASH}-delta.img \
&>/dev/null || return -1
NEW_HASH="$(jq -r '.roothash' ${JSON})"
if [[ $NEW_HASH != $TARGET_HASH ]]; then
xdelta3 -c -d -s /dev/stdin /var/cache/${NAME}/${NAME}-${HASH}-delta.img \
| download_delta_images "$NEW_HASH" "$TARGET_HASH"
else
xdelta3 -c -d -s /dev/stdin /var/cache/${NAME}/${NAME}-${HASH}-delta.img
2018-10-23 16:13:51 +02:00
fi
2018-11-15 16:47:59 +01:00
}
2018-09-07 16:47:54 +02:00
2018-11-15 16:47:59 +01:00
if SIZE=$(check_delta_size "$CURRENT_ROOT_HASH" "$ROOT_HASH") && (($SIZE < $IMAGE_SIZE))
then
dd if=$CURRENT_ROOT_DEV bs=4096 count=$(($CURRENT_IMAGE_SIZE/4096)) \
| download_delta_images "$CURRENT_ROOT_HASH" "$ROOT_HASH" \
| dd bs=4096 conv=fsync status=progress \
of=${ROOT_DEV}-part${NEW_ROOT_PARTNO}
else
curl -C - "${BASEURL}/${NAME}-${ROOT_HASH}.img" \
| dd bs=4096 conv=fsync status=progress \
of=${ROOT_DEV}-part${NEW_ROOT_PARTNO}
fi
2018-11-15 16:47:59 +01:00
jq -r '.rootimgsig' ${JSON} | xxd -r -p > "$MY_TMPDIR/rootimgsig"
2018-09-06 15:56:53 +02:00
2018-11-15 16:47:59 +01:00
if ! dd bs=4096 \
if=${ROOT_DEV}-part${NEW_ROOT_PARTNO} \
count=$(($IMAGE_SIZE/4096)) \
| openssl dgst -sha256 -verify /etc/pki/${NAME}/pubkey \
-signature "$MY_TMPDIR/rootimgsig" /dev/stdin;
then
exit 1
fi
2018-09-06 15:56:53 +02:00
# set the new partition uuids
2018-09-07 16:47:54 +02:00
ROOT_UUID=${ROOT_HASH:32:8}-${ROOT_HASH:40:4}-${ROOT_HASH:44:4}-${ROOT_HASH:48:4}-${ROOT_HASH:52:12}
sfdisk --part-uuid ${ROOT_DEV} ${NEW_ROOT_PARTNO} ${ROOT_UUID}
2018-09-06 15:56:53 +02:00
2018-11-15 16:47:59 +01:00
jq -r '.efitarsig' ${JSON} | xxd -r -p > "$MY_TMPDIR/efitarsig"
curl -C - "${BASEURL}/${NAME}-${ROOT_HASH}-efi.tgz" \
--output "/var/cache/${NAME}/${NAME}-${ROOT_HASH}-efi.tgz"
if ! openssl dgst -sha256 -verify /etc/pki/${NAME}/pubkey \
-signature "$MY_TMPDIR/efitarsig" "${JSONDIR}/${NAME}-${ROOT_HASH}-efi.tgz";
then
rm -f "${JSONDIR}/${NAME}-${ROOT_HASH}-efi.tgz"
exit 1
fi
tar xzf "${JSONDIR}/${NAME}-${ROOT_HASH}-efi.tgz"
2018-09-06 15:56:53 +02:00
# install to /efi
2018-10-23 14:20:04 +02:00
if [[ -d efi/EFI ]]; then
cp -vr efi/EFI/* /efi/EFI/
fi
if [[ ! -f /efi/EFI/Boot/bootx64.efi ]] \
|| cmp --quiet /efi/EFI/${NAME}/${OLD_ROOT_NUM}.efi /efi/EFI/Boot/bootx64.efi \
|| cmp --quiet /efi/EFI/${NAME}/_${OLD_ROOT_NUM}.efi /efi/EFI/Boot/bootx64.efi
then
cp /efi/EFI/${NAME}/bootx64-$ROOT_HASH.efi /efi/EFI/Boot/bootx64.efi
2018-10-23 14:20:04 +02:00
fi
cp /efi/EFI/${NAME}/bootx64-$ROOT_HASH.efi /efi/EFI/${NAME}/${NEW_ROOT_NUM}.efi
2018-09-06 15:56:53 +02:00
2018-10-23 14:20:04 +02:00
if [[ -f /efi/EFI/${NAME}/${OLD_ROOT_NUM}.efi ]]; then
mv /efi/EFI/${NAME}/${OLD_ROOT_NUM}.efi /efi/EFI/${NAME}/_${OLD_ROOT_NUM}.efi
fi
2018-09-11 11:37:47 +02:00
rm -f /efi/EFI/${NAME}/_${NEW_ROOT_NUM}.efi
BOOT_ORDER=$(efibootmgr | grep BootOrder: | { read _ a; echo "$a"; })
BOOT_ORDER=${BOOT_ORDER//FED?,}
BOOT_ORDER=${BOOT_ORDER//FED?}
BOOT_ORDER=${BOOT_ORDER%,}
BOOT_ORDER=${BOOT_ORDER#,}
efibootmgr -o "FED${NEW_ROOT_NUM},FED$((${OLD_ROOT_NUM}+2)),$BOOT_ORDER"
for i in /efi/EFI/${NAME}/bootx64-*.efi; do
[[ $i == /efi/EFI/${NAME}/bootx64-$ROOT_HASH.efi ]] && continue
[[ $i == /efi/EFI/${NAME}/bootx64-$CURRENT_ROOT_HASH.efi ]] && continue
rm -f "$i"
done
echo "Update successful. Reboot your machine to use it."