Skip to content
This repository has been archived by the owner on Jan 30, 2025. It is now read-only.

Commit

Permalink
Implements #16, ISO hash check
Browse files Browse the repository at this point in the history
This commit is a rework from @SibrenVasse pull request #41

- fix a minor bug where ISO check was run twice, before and after sudo
- disable automatic ISO hash check with `--no-hash-check` flag
- exit when hash fails with `--force-hash-check` flag
- explicitly set a hash file with `--hash-file <file>` flag
  • Loading branch information
SibrenVasse authored and jsamr committed Apr 4, 2019
1 parent 2514063 commit 9efdcbf
Show file tree
Hide file tree
Showing 3 changed files with 273 additions and 12 deletions.
250 changes: 240 additions & 10 deletions bootiso
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ if [ -z "$BASH_VERSION" ] || [ "$bashVersion" -lt 4 ]; then
fi

# program constrains definitions
typeset -ar commandDependencies=('lsblk' 'sfdisk' 'mkfs' 'blkid' 'wipefs' 'grep' 'file' 'awk' 'mlabel' 'partprobe' 'tar' 'bc' 'wimlib-imagex')
typeset -ar commandDependencies=('lsblk' 'sfdisk' 'mkfs' 'blkid' 'wipefs' 'blockdev' 'grep' 'file' 'awk' 'mlabel' 'syslinux' 'rsync'
'partprobe' 'curl' 'tar' 'bc' 'wimlib-imagex' 'md5sum' 'sha1sum' 'sha256sum' 'sha512sum' 'cut')
typeset -Ar commandPackages=(
['lsblk']='util-linux'
['sfdisk']='util-linux'
Expand All @@ -38,6 +39,11 @@ typeset -Ar commandPackages=(
['tar']='tar'
['bc']='bc'
['wimlib-imagex']='wimlib'
['md5sum']='coreutils'
['sha1sum']='coreutils'
['sha256sum']='coreutils'
['sha512sum']='coreutils'
['cut']='coreutils'
)
typeset shortOptions='bydJahlMftLp'
typeset -ar supportedFS=('vfat' 'exfat' 'ntfs' 'ext2' 'ext3' 'ext4' 'f2fs')
Expand All @@ -54,6 +60,8 @@ typeset -Ar userFlagsCompatibilityMatrix=(
['no-eject']='install-auto install-mount-rsync install-dd format'
['autoselect']='install-auto install-mount-rsync install-dd format'
['no-mime-check']='install-auto install-mount-rsync install-dd'
['no-hash-check']='install-auto install-mount-rsync install-dd probe inspect'
['force-hash-check']='install-auto install-mount-rsync install-dd probe inspect'
['no-usb-check']='install-auto install-mount-rsync install-dd list-usb-drives probe format'
['no-size-check']='install-auto install-mount-rsync install-dd'
)
Expand Down Expand Up @@ -104,10 +112,13 @@ typeset -A userFlags=(
['no-mime-check']=''
['no-usb-check']=''
['no-size-check']=''
['no-hash-check']=''
['force-hash-check']=''
['no-wimsplit']=''
)
typeset -A userVars=(
['iso-file']=''
['hash-file']=''
['device']=''
['type']=''
['label']=''
Expand All @@ -116,9 +127,10 @@ typeset -A userVars=(

# user defined variables
typeset selectedIsoFile # no default
typeset selectedDevice # default to prompted to user
typeset partitionLabel # default to inferred from ISO file label
typeset partitionType # default to vfat
typeset hashFile
typeset selectedDevice # default to prompted to user
typeset partitionLabel # default to inferred from ISO file label
typeset partitionType # default to vfat
typeset action='install-auto'
typeset selectedBootloaderVersion # default to auto

Expand All @@ -127,6 +139,8 @@ typeset disableMimeCheck
typeset disableUSBCheck
typeset disableSizeCheck
typeset disableConfirmation
typeset disableHashCheck
typeset forceHashCheck
typeset disableWimsplit
typeset autoselect
typeset shouldMakePartition
Expand Down Expand Up @@ -205,6 +219,10 @@ OPTION FLAGS
--no-size-check $scriptName won't assert that the device size is larger
than the image. $(redify 'Use at your own risk.')
--no-wimsplit Prevent splitting sources/install.wim file in Windows ISOs.
--no-hash-check Do not search for hash files and check ISO integrity.
--force-hash-check Fail and exit when no valid hash is found. Cannot be used with '--no-hash-check'.
--hash-file <file> Specify file containing the ISO hash. Other files will not be checked.
Cannot be used with '--no-hash-check'
--local-bootloader Prevent download of remote bootloader and force local (SYSLINUX) during
installation. Applicable to [install-mount-rsync] action.
--remote-bootloader <version> Force download of remote bootloader at version <version>.
Expand Down Expand Up @@ -380,8 +398,7 @@ checkSudo() {
failISOCheck() {
echoerr "Provided file '$selectedIsoFile' doesn't seem to be an ISO file (wrong mime-type: '$mimetype')."
echowarn "You can bypass this policy with '-M, --no-mime-check', but it is likely that the operation will fail."
echoerr "Exiting..."
exit 1
failAndExit "Exiting..."
}

assertISOIsOK() {
Expand All @@ -405,6 +422,193 @@ assertISOIsOK() {
failISOCheck
fi
fi

if [ "$disableHashCheck" == 'false' ]; then
checkISOHash
fi
}

askForExit() {
typeset answer
read -r -n1 -p "Do you still want to continue? (y/n)> " answer
echo
case $answer in
y | Y)
return
;;
*)
failAndExit
;;
esac
}

checkISOHash() {
checkHash() {
typeset hashPath=$1 # Path to file containing hashes
typeset isoName=$2 # File to be checked
typeset hashName=$3 # Name of command of hash

# Hash from hash file
typeset gHash=$(awk -v pattern="$isoName$" '$0~pattern { print $1; exit }' "$hashPath")
if [ -z "$gHash" ]; then
echoerr "No matching filename found in hash file '$hashPath'"
return
elif [ -z "$hashName" ]; then
case ${#gHash} in
32)
hashName="md5sum"
;;
40)
hashName="sha1sum"
;;
64)
hashName="sha256sum"
;;
128)
hashName="sha512sum"
;;
*)
echowarn "Unknown hash type found in file '$hashPath'"
if [ "$forceHashCheck" == 'true' ]; then
failAndExit "Unknown hash type found found in '$hashPath'"
else
echowarn "Unknown hash type found found in '$hashPath'"
askForExit
echoinfo "Ignoring unknown hash type."
fi
;;
esac
fi

typeset lHash=$($hashName "$isoName" | awk "{print \$1; exit }") # Hash from iso
if [ "$gHash" != "$lHash" ]; then
if [ "$forceHashCheck" == 'true' ]; then
failAndExit "Incorrect ${hashName%sum} hash found in '$hashPath'"
else
echowarn "Incorrect hash found found in '$hashPath'"
askForExit
echoinfo "Ignoring incorrect hash."
fi
else
echogood "Correct ${hashName%sum} hash found in '$hashPath'"
numValidHashes=$((numValidHashes + 1))
fi
}

typeset numValidHashes=0
typeset isoDirectory=$(dirname "$selectedIsoFile")
typeset isoFileName=$(basename "$selectedIsoFile")
typeset -ar hashes=("md5sum" "sha1sum" "sha256sum" "sha512sum")

if [ -n "$hashFile" ]; then
if [ -a "$hashFile" ]; then
checkHash "$hashFile" "$selectedIsoFile"
else
failAndExit "Specified hash file '$hashFile' does not exist."
fi
else
shopt -s nullglob nocaseglob
for hash in "${hashes[@]}"; do
for file in "$isoDirectory/$hash"*; do
checkHash "$file" "$isoFileName" "$hash"
done

if [ -a "$selectedIsoFile.${hash%sum}" ]; then
checkHash "$selectedIsoFile.${hash%sum}" "$isoFileName" "$hash"
fi
done
shopt -u nullglob nocaseglob
fi

if [ "$forceHashCheck" == 'true' ] && [ $numValidHashes == 0 ]; then
failAndExit "No valid hashes found. Assert forced by '--force-hash-check'"
fi
}

checkISOHash() {
checkHash() {
typeset hashPath=$1 # Path to file containing hashes
typeset isoName=$2 # File to be checked
typeset hashName=$3 # Name of command of hash

# Hash from hash file
typeset gHash=$(awk -v pattern="$isoName$" '$0 ~ pattern { print $1; exit }' "$hashPath")
if [ -z "$gHash" ]; then
echoerr "No matching filename found in hash file '$hashPath'"
return
elif [ -z "$hashName" ]; then
case ${#gHash} in
32)
hashName="md5sum"
;;
40)
hashName="sha1sum"
;;
64)
hashName="sha256sum"
;;
128)
hashName="sha512sum"
;;
*)
failAndExit "Matching line in '$hashPath' has an unexpected hash format."
;;
esac
fi

typeset lHash=$($hashName "$isoName" | awk "{print \$1; exit }") # Hash from iso
if [ "$gHash" != "$lHash" ]; then
if [ "$forceHashCheck" == 'true' ]; then
failAndExit "Hash mismatch in '$hashPath' (${hashName%sum})."
else
echowarn "Hash mismatch in '$hashPath' (${hashName%sum})."
typeset answer
read -r -n1 -p "Do you still want to continue? (y/n)> " answer
echo
case $answer in
y | Y)
return
;;
*)
failAndExit
;;
esac
echoinfo "Ignoring mismatching hash."
fi
else
echogood "Matching ${hashName%sum} hash found in '$hashPath'"
numValidHashes=$((numValidHashes + 1))
fi
}

typeset numValidHashes=0
typeset isoDirectory=$(dirname "$selectedIsoFile")
typeset isoFileName=$(basename "$selectedIsoFile")
typeset -ar hashes=("md5sum" "sha1sum" "sha256sum" "sha512sum")

if [ -n "$hashFile" ]; then
if [ -f "$hashFile" ]; then
checkHash "$hashFile" "$isoFileName"
else
failAndExit "Specified hash file '$hashFile' does not exist."
fi
else
shopt -s nullglob nocaseglob
for hash in "${hashes[@]}"; do
for file in "$isoDirectory/$hash"*; do
checkHash "$file" "$isoFileName" "$hash"
done

if [ -f "$selectedIsoFile.${hash%sum}" ]; then
checkHash "$selectedIsoFile.${hash%sum}" "$isoFileName" "$hash"
fi
done
shopt -u nullglob nocaseglob
fi

if [ "$forceHashCheck" == 'true' ] && [ $numValidHashes == 0 ]; then
failAndExit "No matching hashes found. Assert forced by '--force-hash-check'"
fi
}

firstMatchInFolder() {
Expand Down Expand Up @@ -651,6 +855,13 @@ parseArguments() {
setUserVar 'label' "$2"
shift 2
;;
--hash-file)
if (($# < 2)); then
failAndExit "Missing value for '$1' flag. Please provide a hash file."
fi
setUserVar 'hash-file' "$2"
shift 2
;;
-J | --no-eject)
enableUserFlag 'no-eject'
shift
Expand All @@ -671,6 +882,14 @@ parseArguments() {
enableUserFlag 'no-size-check'
shift
;;
--no-hash-check)
enableUserFlag 'no-hash-check'
shift
;;
--force-hash-check)
enableUserFlag 'force-hash-check'
shift
;;
--no-wimsplit)
enableUserFlag 'no-wimsplit'
shift
Expand Down Expand Up @@ -1356,7 +1575,17 @@ checkUserVars() {
checkUserFlags() {
# Autoselect security
if [ "${userFlags['autoselect']}" == 'true' ] && [ "${userFlags['no-usb-check']}" == 'true' ]; then
failAndExit "You cannot set '-a, --autoselect' while disabling USB check with '--no-usb-check'."
failAndExit "You cannot set '-a, --autoselect' while disabling USB check with '--no-usb-check'"
fi
if [ "${userFlags['no-hash-check']}" == 'true' ] && [ "${userFlags['force-hash-check']}" == 'true' ]; then
failAndExit "You cannot combine '--no-hash-check' and '--force-hash-check'"
elif [ "${userFlags['no-hash-check']}" == 'true' ] && [ -n "${userVars['hash-file']}" ]; then
failAndExit "You cannot combine '--no-hash-check' and '--hash-file'"
fi
if [ "${userFlags['no-hash-check']}" == 'true' ] && [ "${userFlags['force-hash-check']}" == 'true' ]; then
failAndExit "You cannot combine '--no-hash-check' and '--force-hash-check'"
elif [ "${userFlags['no-hash-check']}" == 'true' ] && [ -z "${userVars['remote-bootloader']}" ]; then
failAndExit "You cannot combine '--no-hash-check' and '--hash-file'"
fi
# warnings (only with sudo)
if ((EUID == 0)); then
Expand Down Expand Up @@ -1446,9 +1675,12 @@ assignInternalVariables() {
disableMimeCheck=${userFlags['no-mime-check']:-'false'}
disableUSBCheck=${userFlags['no-usb-check']:-'false'}
disableSizeCheck=${userFlags['no-size-check']:-'false'}
disableHashCheck=${userFlags['no-hash-check']:-'false'}
forceHashCheck=${userFlags['force-hash-check']:-'false'}
disableWimsplit=${userFlags['no-wimsplit']:-'false'}
# Vars flags
partitionType=${userVars['type']:-'vfat'}
hashFile=${userVars['hash-file']:-''}
selectedDevice=${userVars['device']:-''}
partitionLabel=${userVars['label']:-''}
selectedBootloaderVersion=${userVars['remote-bootloader']:-'auto'}
Expand Down Expand Up @@ -1717,14 +1949,12 @@ main() {
assignInternalVariables
checkFlagMatrix
checkPackages
if [ "$expectingISOFile" == 'true' ]; then
assertISOIsOK
fi
if [ "$requiresRoot" == 'true' ]; then
checkSudo "$@"
configureFolders
fi
if [ "$expectingISOFile" == 'true' ]; then
assertISOIsOK
inspectISO
fi
case "$action" in
Expand Down
13 changes: 13 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# next

**Features**

- check ISO hash automatically, #16
- disable automatic ISO hash check with `--no-hash-check` flag
- exit when hash fails with `--force-hash-check` flag
- explicitly set a hash file with `--hash-file <file>` flag

**Bugfixes**

- fix a minor bug where ISO check was run twice, before and after sudo

# v3.2.2

- fix typos + rewording messages (@SibrenVasse)
Expand Down
Loading

0 comments on commit 9efdcbf

Please sign in to comment.