You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
keylime/SOURCES/0001-Restore-create-allowli...

390 lines
14 KiB

--- a/scripts/create_runtime_policy.sh 2024-01-30 18:17:19.000000000 +0100
+++ b/scripts/create_runtime_policy.sh 2024-08-16 17:25:50.871701853 +0200
@@ -1,282 +1,155 @@
-#!/usr/bin/env bash
+#!/usr/bin/bash
################################################################################
# SPDX-License-Identifier: Apache-2.0
# Copyright 2017 Massachusetts Institute of Technology.
################################################################################
-
-if [ $0 != "-bash" ] ; then
- pushd `dirname "$0"` > /dev/null 2>&1
-fi
-KCRP_BASE_DIR=$(pwd)
-if [ $0 != "-bash" ] ; then
- popd 2>&1 > /dev/null
-fi
-KCRP_BASE_DIR=$KCRP_BASE_DIR/..
-
-function detect_hash {
- local hashstr=$1
-
- case "${#hashstr}" in
- 32) hashalgo=md5sum ;;
- 40) hashalgo=sha1sum ;;
- 64) hashalgo=sha256sum ;;
- 128) hashalgo=sha512sum ;;
- *) hashalgo="na";;
- esac
-
- echo $hashalgo
-}
-
-function announce {
- # 1 - MESSAGE
-
- MESSAGE=$(echo "${1}" | tr '\n' ' ')
- MESSAGE=$(echo $MESSAGE | sed "s/\t\t*/ /g")
-
- echo "==> $(date) - ${0} - $MESSAGE"
-}
-
-function valid_algo {
- local algo=$1
-
- [[ " ${ALGO_LIST[@]} " =~ " ${algo} " ]]
-}
-
# Configure the installer here
INITRAMFS_TOOLS_GIT=https://salsa.debian.org/kernel-team/initramfs-tools.git
INITRAMFS_TOOLS_VER="master"
-# All defaults
-ALGO=sha1sum
-WORK_DIR=/tmp/kcrp
-OUTPUT_DIR=${WORK_DIR}/output
-ALLOWLIST_DIR=${WORK_DIR}/allowlist
-INITRAMFS_LOC="/boot/"
-INITRAMFS_STAGING_DIR=${WORK_DIR}/ima_ramfs/
-INITRAMFS_TOOLS_DIR=${WORK_DIR}/initramfs-tools
-BOOT_AGGREGATE_LOC="/sys/kernel/security/ima/ascii_runtime_measurements"
-ROOTFS_LOC="/"
-EXCLUDE_LIST="none"
-SKIP_PATH="none"
-ALGO_LIST=("sha1sum" "sha256sum" "sha512sum")
+WORKING_DIR=$(readlink -f "$0")
+WORKING_DIR=$(dirname "$WORKING_DIR")
# Grabs Debian's initramfs_tools from Git repo if no other options exist
if [[ ! `command -v unmkinitramfs` && ! -x "/usr/lib/dracut/skipcpio" ]] ; then
# Create temp dir for pulling in initramfs-tools
- announce "INFO: Downloading initramfs-tools: $INITRAMFS_TOOLS_DIR"
+ TMPDIR=`mktemp -d` || exit 1
+ echo "INFO: Downloading initramfs-tools: $TMPDIR"
- mkdir -p $INITRAMFS_TOOLS_DIR
# Clone initramfs-tools repo
- pushd $INITRAMFS_TOOLS_DIR > /dev/null 2>&1
- git clone $INITRAMFS_TOOLS_GIT initramfs-tools > /dev/null 2>&1
- pushd initramfs-tools > /dev/null 2>&1
- git checkout $INITRAMFS_TOOLS_VER > /dev/null 2>&1
- popd > /dev/null 2>&1
- popd > /dev/null 2>&1
+ pushd $TMPDIR
+ git clone $INITRAMFS_TOOLS_GIT initramfs-tools
+ pushd initramfs-tools
+ git checkout $INITRAMFS_TOOLS_VER
+ popd # $TMPDIR
+ popd
shopt -s expand_aliases
- alias unmkinitramfs=$INITRAMFS_TOOLS_DIR/initramfs-tools/unmkinitramfs
-
- which unmkinitramfs > /dev/null 2>&1 || exit 1
+ alias unmkinitramfs=$TMPDIR/initramfs-tools/unmkinitramfs
fi
+
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
exit 1
fi
-USAGE=$(cat <<-END
- Usage: $0 -o/--output_file FILENAME [-a/--algo ALGO] [-x/--ramdisk-location PATH] [-y/--boot_aggregate-location PATH] [-z/--rootfs-location PATH] [-e/--exclude_list FILENAME] [-s/--skip-path PATH] [-h/--help]
+if [ $# -lt 1 ]
+then
+ echo "No arguments provided" >&2
+ echo "Usage: `basename $0` -o [filename] -h [hash-algo]" >&2
+ exit $NOARGS;
+fi
- optional arguments:
- -a/--algo (checksum algorithm to be used, default: $ALGO)
- -x/--ramdisk-location (path to initramdisk, default: $INITRAMFS_LOC, set to "none" to skip)
- -y/--boot_aggregate-location (path for IMA log, used for boot aggregate extraction, default: $BOOT_AGGREGATE_LOC, set to "none" to skip)
- -z/--rootfs-location (path to root filesystem, default: $ROOTFS_LOC, cannot be skipped)
- -e/--exclude_list (filename containing a list of paths to be excluded (i.e., verifier will not try to match checksums, default: $EXCLUDE_LIST)
- -s/--skip-path (comma-separated path list, files found there will not have checksums calculated, default: $SKIP_PATH)
- -h/--help (show this message and exit)
-END
-)
+ALGO=sha256sum
-while [[ $# -gt 0 ]]
-do
- key="$1"
+ALGO_LIST=("sha1sum" "sha256sum" "sha512sum")
+
+valid_algo() {
+ local algo=$1
- case $key in
- -a|--algo)
- ALGO="$2"
- shift
- ;;
- -a=*|--algo=*)
- ALGO=$(echo $key | cut -d '=' -f 2)
- ;;
- -x|--ramdisk-location)
- INITRAMFS_LOC="$2"
- shift
- ;;
- -x=*|--ramdisk-location=*)
- INITRAMFS_LOC=$(echo $key | cut -d '=' -f 2)
- ;;
- -y|--boot_aggregate-location)
- BOOT_AGGREGATE_LOC=$2
- shift
- ;;
- -y=*|--boot_aggregate-location=*)
- BOOT_AGGREGATE_LOC=$(echo $key | cut -d '=' -f 2)
- ;;
- -z|--rootfs-location)
- ROOTFS_LOC=$2
- shift
- ;;
- -z=*|--rootfs-location=*)
- ROOTFS_LOC=$(echo $key | cut -d '=' -f 2)
- ;;
- -e|--exclude_list)
- EXCLUDE_LIST=$2
- shift
- ;;
- -e=*|--exclude_list=*)
- EXCLUDE_LIST=$(echo $key | cut -d '=' -f 2)
- ;;
- -o=*|--output_file=*)
- OUTPUT=$(echo $key | cut -d '=' -f 2)
- ;;
- -o|--output_file)
- OUTPUT=$2
- shift
- ;;
- -s=*|--skip-path=*)
- SKIP_PATH=$(echo $key | cut -d '=' -f 2)
- ;;
- -s|--skip-path)
- SKIP_PATH=$2
- shift
- ;;
- -h|--help)
- printf "%s\n" "$USAGE"
- exit 0
- shift
- ;;
- *)
- # unknown option
- ;;
- esac
- shift
+ [[ " ${ALGO_LIST[@]} " =~ " ${algo} " ]]
+}
+
+while getopts ":o:h:" opt; do
+ case $opt in
+ o)
+ OUTPUT=$(readlink -f $OPTARG)
+ rm -f $OUTPUT
+ ;;
+ h)
+ if valid_algo $OPTARG; then
+ ALGO=$OPTARG
+ else
+ echo "Invalid hash function argument: use sha1sum, sha256sum, or sha512sum"
+ exit 1
+ fi
+ ;;
+ esac
done
-if ! valid_algo $ALGO
+if [ ! "$OUTPUT" ]
then
- echo "Invalid hash function argument: pick from \"${ALGO_LIST[@]}\""
+ echo "Missing argument for -o" >&2;
+ echo "Usage: $0 -o [filename] -h [hash-algo]" >&2;
exit 1
fi
-if [[ -z $OUTPUT ]]
-then
- printf "%s\n" "$USAGE"
- exit 1
+
+# Where to look for initramfs image
+INITRAMFS_LOC="/boot"
+if [ -d "/ostree" ]; then
+ # If we are on an ostree system change where we look for initramfs image
+ loc=$(grep -E "/ostree/[^/]([^/]*)" -o /proc/cmdline | head -n 1 | cut -d / -f 3)
+ INITRAMFS_LOC="/boot/ostree/${loc}/"
fi
-rm -rf $ALLOWLIST_DIR
-rm -rf $INITRAMFS_STAGING_DIR
-rm -rf $OUTPUT_DIR
-announce "Writing allowlist $ALLOWLIST_DIR/${OUTPUT} with $ALGO..."
-mkdir -p $ALLOWLIST_DIR
+echo "Writing allowlist to $OUTPUT with $ALGO..."
-if [[ $BOOT_AGGREGATE_LOC != "none" ]]
-then
- announce "--- Adding boot agregate from $BOOT_AGGREGATE_LOC on allowlist $ALLOWLIST_DIR/${OUTPUT} ..."
# Add boot_aggregate from /sys/kernel/security/ima/ascii_runtime_measurements (IMA Log) file.
# The boot_aggregate measurement is always the first line in the IMA Log file.
# The format of the log lines is the following:
# <PCR_ID> <PCR_Value> <IMA_Template> <File_Digest> <File_Name> <File_Signature>
# File_Digest may start with the digest algorithm specified (e.g "sha1:", "sha256:") depending on the template used.
- head -n 1 $BOOT_AGGREGATE_LOC | awk '{ print $4 " boot_aggregate" }' | sed 's/.*://' >> $ALLOWLIST_DIR/${OUTPUT}
+head -n 1 /sys/kernel/security/ima/ascii_runtime_measurements | awk '{ print $4 " boot_aggregate" }' | sed 's/.*://' >> $OUTPUT
- bagghash=$(detect_hash $(cat $ALLOWLIST_DIR/${OUTPUT} | cut -d ' ' -f 1))
- if [[ $ALGO != $bagghash ]]
- then
- announce "ERROR: \"boot aggregate\" has was calculated with $bagghash, but files will be calculated with $ALGO. Use option -a $bagghash"
- exit 1
- fi
-else
- announce "--- Skipping boot aggregate..."
-fi
-
-announce "--- Adding all appropriate files from $ROOTFS_LOC on allowlist $ALLOWLIST_DIR/${OUTPUT} ..."
# Add all appropriate files under root FS to allowlist
-pushd $ROOTFS_LOC > /dev/null 2>&1
-BASE_EXCLUDE_DIRS="\bsys\b\|\brun\b\|\bproc\b\|\blost+found\b\|\bdev\b\|\bmedia\b\|\bsnap\b\|\bmnt\b\|\bvar\b\|\btmp\b"
-ROOTFS_FILE_LIST=$(ls | grep -v $BASE_EXCLUDE_DIRS)
-if [[ $SKIP_PATH != "none" ]]
-then
- SKIP_PATH=$(echo $SKIP_PATH | sed -e "s#^$ROOTFS_LOC##g" -e "s#,$ROOTFS_LOC##g" -e "s#,#\\\|#g")
- ROOTFS_FILE_LIST=$(echo "$ROOTFS_FILE_LIST" | grep -v "$SKIP_PATH")
-fi
-find $ROOTFS_FILE_LIST \( -fstype rootfs -o -xtype f -type l -o -type f \) -uid 0 -exec $ALGO "$ROOTFS_LOC/{}" >> $ALLOWLIST_DIR/${OUTPUT} \;
-popd > /dev/null 2>&1
+cd /
+find `ls / | grep -v "\bsys\b\|\brun\b\|\bproc\b\|\blost+found\b\|\bdev\b\|\bmedia\b\|\bsnap\b\|mnt"` \( -fstype rootfs -o -xtype f -type l -o -type f \) -uid 0 -exec $ALGO '/{}' >> $OUTPUT \;
# Create staging area for init ram images
-mkdir -p $INITRAMFS_STAGING_DIR
-
-if [[ $INITRAMFS_LOC != "none" ]]
-then
- # Where to look for initramfs image
- if [[ -d "/ostree" ]]
- then
- X=$INITRAMFS_LOC
- # If we are on an ostree system change where we look for initramfs image
- loc=$(grep -E "/ostree/[^/]([^/]*)" -o /proc/cmdline | head -n 1 | cut -d / -f 3)
- INITRAMFS_LOC="/boot/ostree/${loc}/"
- announce "--- The location of initramfs was overriden from \"${X}\" to \"$INITRAMFS_LOC\""
- fi
+rm -rf /tmp/ima/
+mkdir -p /tmp/ima
- announce "--- Creating allowlist for init ram disks found under \"$INITRAMFS_LOC\" to $ALLOWLIST_DIR/${OUTPUT} ..."
- for i in $(ls ${INITRAMFS_LOC}/initr* 2> /dev/null)
- do
- announce " extracting $i"
- mkdir -p $INITRAMFS_STAGING_DIR/$i-extracted
- cd $INITRAMFS_STAGING_DIR/$i-extracted
-
- # platform-specific handling of init ram disk images
- if [[ `command -v unmkinitramfs` ]] ; then
- mkdir -p $INITRAMFS_STAGING_DIR/$i-extracted-unmk
- unmkinitramfs $i $INITRAMFS_STAGING_DIR/$i-extracted-unmk
- if [[ -d "$INITRAMFS_STAGING_DIR/$i-extracted-unmk/main/" ]] ; then
- cp -r $INITRAMFS_STAGING_DIR/$i-extracted-unmk/main/. /tmp/ima/$i-extracted
- else
- cp -r $INITRAMFS_STAGING_DIR/$i-extracted-unmk/. /tmp/ima/$i-extracted
- fi
- elif [[ -x "/usr/lib/dracut/skipcpio" ]] ; then
- /usr/lib/dracut/skipcpio $i | gunzip -c | cpio -i -d 2> /dev/null
+# Iterate through init ram disks and add files to allowlist
+echo "Creating allowlist for init ram disk"
+for i in `ls ${INITRAMFS_LOC}/initr*`
+do
+ echo "extracting $i"
+ mkdir -p /tmp/ima/$i-extracted
+ cd /tmp/ima/$i-extracted
+
+ # platform-specific handling of init ram disk images
+ if [[ `command -v unmkinitramfs` ]] ; then
+ mkdir -p /tmp/ima/$i-extracted-unmk
+ unmkinitramfs $i /tmp/ima/$i-extracted-unmk
+ if [[ -d "/tmp/ima/$i-extracted-unmk/main/" ]] ; then
+ cp -r /tmp/ima/$i-extracted-unmk/main/. /tmp/ima/$i-extracted
else
- announce "ERROR: No tools for initramfs image processing found!"
- exit 1
+ cp -r /tmp/ima/$i-extracted-unmk/. /tmp/ima/$i-extracted
fi
+ elif [[ -x "/usr/lib/dracut/skipcpio" ]] ; then
+ /usr/lib/dracut/skipcpio $i | gunzip -c 2> /dev/null | cpio -i -d 2> /dev/null
+ else
+ echo "ERROR: No tools for initramfs image processing found!"
+ break
+ fi
- find -type f -exec $ALGO "./{}" \; | sed "s| \./\./| /|" >> $ALLOWLIST_DIR/${OUTPUT}
- done
-fi
+ find -type f -exec $ALGO "./{}" \; | sed "s| \./\./| /|" >> $OUTPUT
+done
-# Non-critical cleanup on the resulting file (when ROOTFS_LOC = '/', the path starts on allowlist ends up with double '//' )
-sed -i "s^ //^ /^g" $ALLOWLIST_DIR/${OUTPUT}
-# A bit of cleanup on the resulting file (among other problems, sha256sum might output a hash with the prefix '\\')
-sed -i "s/^\\\//g" $ALLOWLIST_DIR/${OUTPUT}
-
-# Convert to runtime policy
-mkdir -p $OUTPUT_DIR
-announce "Converting created allowlist ($ALLOWLIST_DIR/${OUTPUT}) to Keylime runtime policy ($OUTPUT_DIR/${OUTPUT}) ..."
-CONVERT_CMD_OPTS="--allowlist $ALLOWLIST_DIR/${OUTPUT} --output_file $OUTPUT_DIR/${OUTPUT}"
-[ -f $EXCLUDE_LIST ] && CONVERT_CMD_OPTS="$CONVERT_CMD_OPTS --excludelist $EXCLUDE_LIST"
-
-pushd $KCRP_BASE_DIR > /dev/null 2>&1
-export PYTHONPATH=$KCRP_BASE_DIR:$PYTHONPATH
-# only 3 dependencies required: pip3 install cryptography lark packaging
-python3 ./keylime/cmd/convert_runtime_policy.py $CONVERT_CMD_OPTS; echo " "
-if [[ $? -eq 0 ]]
-then
- announce "Done, new runtime policy file present at ${OUTPUT_DIR}/$OUTPUT. It can be used on the tenant keylime host with \"keylime_tenant -c add --runtime-policy ${OUTPUT_DIR}/$OUTPUT <other options>"
-fi
-popd > /dev/null 2>&1
+# when ROOTFS_LOC = '/', the path starts on allowlist ends up with double '//'
+#
+# Example:
+#
+# b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c //bar
+#
+# Replace the unwanted '//' with a single '/'
+sed -i 's| /\+| /|g' ${OUTPUT}
+
+# When the file name contains newlines or backslashes, the output of sha256sum
+# adds a backslash at the beginning of the line.
+#
+# Example:
+#
+# $ echo foo > ba\\r
+# $ sha256sum ba\\r
+# \b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c ba\\r
+#
+# Remove the unwanted backslash prefix
+sed -i 's/^\\//g' ${OUTPUT}
+
+# Clean up
+rm -rf /tmp/ima