#!/usr/bin/env bash

# chkboot: copy to $PATH and change its permissions to executable, then set to run on startup
#
# author: ju (ju at heisec dot de)
# contributors: inhies, prurigro
#
# license: GPLv2
#
# a reminder that this will NOT protect against:
#   -a trojan hiding in your BIOS
#   -rootkits that mimmick the old files

CHKBOOT_CMD=$(echo "$0" | sed 's/.*\///g')

function help {
    echo "Syntax:"
    echo -e "\t'${CHKBOOT_CMD}': Update checksums for boot partition files and create/update the alert file with any differences"
    echo -e "\t'${CHKBOOT_CMD} -u' or '${CHKBOOT_CMD} --update': When making valid changes to boot, update checksums and remove the alert file if it exists"
    echo -e "\t'${CHKBOOT_CMD} -h' or '${CHKBOOT_CMD} --help': Display this help text"
}

if [ "$UID" -ne 0 ]; then
    echo "${CHKBOOT_CMD} must be run as root"
    exit 1
fi

source /etc/default/chkboot.conf

CURRTIME=`date +"%Y%m%d-%H%M%S"`
BOOTFILES="${CHKBOOT_DATA}/BOOTFILES-$CURRTIME"
BOOTFILES_LAST="${CHKBOOT_DATA}/BOOTFILES-last"
DISKHEAD="${CHKBOOT_DATA}/DISKHEAD-$CURRTIME"
DISKHEAD_LAST="${CHKBOOT_DATA}/DISKHEAD-last"
CHANGES_ALERT="${CHKBOOT_DATA}/${CHANGES_ALERT}"
CHANGES_LOG="${CHKBOOT_DATA}/${CHANGES_LOG}"

UPDATE="0"
CHANGED="0"
if [ ! -z "$1" ]; then
    if [ "$1" = "-u" -o "$1" = "--update" ]; then
        UPDATE="1"
    elif [ "$1" = "-h" -o "$1" = "--help" ]; then
        help
        exit 0
    else
        echo -e "Invalid argument: ${1}"
        help
        exit 1
    fi
fi

install -d "$CHKBOOT_DATA"

# delete the previous
if [[ -s "${CHANGES_ALERT}" ]]; then
    # restore /etc/issue if it's been modified
    if [ ! $(grep -c "CHKBOOT ALERT" /etc/issue) = 0 ]; then
        sed -i ':a;N;$!ba;s/\n/CHKBOOT_TEMPNEWLINE/g;s:CHKBOOT_TEMPNEWLINE\x1B\[[0-9;]*[mK].*::g;s/CHKBOOT_TEMPNEWLINE/\n/g' /etc/issue
    fi

    # reset the changes being tracked by removing the file containing them (the log still exists)
    rm "${CHANGES_ALERT}"
fi

# making a copy of the head of the disk
dd if="$BOOTDISK" of="$DISKHEAD" bs=512 count=1 > /dev/null 2>&1

pushd "$BOOTDIR" > /dev/null 2>&1
    files=`find . -type f` # get file infos
    files=`echo $files | sed "s/.\/grub\/grubenv//"` # remove files that should be skipped

    # generate hashes of each file
    for fname in $files; do
        hash=`sha512sum -b $fname | awk '{print $1}'`
        echo "$fname $hash"
    done > $BOOTFILES

    [ -s "$BOOTFILES" ] || ( exit 0 )
    if [ ! -s "$DISKHEAD_LAST" ]; then ln -s -f $DISKHEAD $DISKHEAD_LAST; fi
    if [ ! -s "$BOOTFILES_LAST" ]; then ln -s -f $BOOTFILES $BOOTFILES_LAST; exit 0; fi

    ( diff $BOOTFILES_LAST $BOOTFILES >> "${CHANGES_ALERT}-now" ) || CHANGED="1"
    ( diff $DISKHEAD_LAST $DISKHEAD >> "${CHANGES_ALERT}-now" ) || CHANGED="1"

    # changes detected, create the changes alert file
    if [ $CHANGED = "0" ] ; then
        rm -f $DISKHEAD "${CHANGES_ALERT}-now" $BOOTFILES
    else
        # create the changes alert file with a heading and the date and the list of changed files
        echo -e "List of changed files on `date`:\n" > "${CHANGES_ALERT}"
        cat "${CHANGES_ALERT}-now" >> "$CHANGES_ALERT"

        # copy the latest changes file to the end of the log
        cat "$CHANGES_ALERT" >> "$CHANGES_LOG"
        echo >> "$CHANGES_LOG"

        # set the latest hashes to the old ones for next time then exit
        ln -s -f $BOOTFILES $BOOTFILES_LAST
        ln -s -f $DISKHEAD $DISKHEAD_LAST
    fi

    if [ $UPDATE = "1" ]; then
        rm -f "$CHANGES_ALERT" "${CHANGES_ALERT}-now"
        exit 0
    fi

    exit $CHANGED

popd > /dev/null 2>&1
