blob: 12bfbe5db517b6b3ba19af0304058aff3950ea88 [file] [log] [blame] [edit]
#!/bin/bash
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Abort on error.
set -e
# Load common constants and variables.
. "$(dirname "$0")/common.sh"
# Given a kernel boot param string which includes ...dm="dmstuff"...
# this returns the dmstuff by itself.
get_dmparams() {
echo "$1" | sed 's/^.*\ dm="\([^"]*\)".*/\1/'
}
# Given a kernel boot param string which includes ...dm="stuff"...
# this returns the param string with the dm="..." section removed.
# Useful in conjunction with get_dmparams to divide and process
# the two sections of parameters in seperate passes
kparams_remove_dm() {
echo "$1" | sed 's/dm="[^"]*"//'
}
# Given a dm param string which includes dynamic values, return the
# same string with these values replaced by a magic string placeholder.
# This same magic placeholder is used in the config file, for comparison
# purposes.
dmparams_mangle() {
local dmparams=$1
# First handle new key-value style verity parameters.
dmparams=$(echo "$dmparams" |
sed -e 's/root_hexdigest=[0-9a-fA-F]*/root_hexdigest=MAGIC_HASH/' |
sed -e 's/salt=[0-9a-fA-F]*/salt=MAGIC_SALT'/)
# If we didn't substitute the MAGIC_HASH yet, these are the old
# verity parameter format.
if [[ $dmparams != *MAGIC_HASH* ]]; then
dmparams=$(echo $dmparams | sed 's/sha1 [0-9a-fA-F]*/sha1 MAGIC_HASH/')
fi
# If we have bootcache enabled, replace its copy of the root_hexdigest
# with MAGIC_HASH. The parameter is positional.
if [[ $dmparams == *bootcache* ]]; then
dmparams=$(echo $dmparams |
sed -r 's:(bootcache (PARTUUID=)?%U(/PARTNROFF=|\+)1 [0-9]+) [0-9a-fA-F]+:\1 MAGIC_HASH:')
fi
echo $dmparams
}
# This escapes any non-alphanum character, since many such characters
# are regex metacharacters.
escape_regexmetas() {
echo "$1" | sed 's/\([^a-zA-Z0-9]\)/\\\1/g'
}
usage() {
echo "Usage $PROG image [config]"
}
main() {
# We want to catch all the discrepancies, not just the first one.
# So, any time we find one, we set testfail=1 and continue.
# When finished we will use testfail to determine our exit value.
local testfail=0
# A buffer to include useful information that we dump when things fail.
local output
# Copy of a string before it has been through sed
local pre_sed
# Where the disk image is mounted.
local loopdev
if [[ $# -ne 1 ]] && [[ $# -ne 2 ]]; then
usage
exit 1
fi
local image="$1"
# A byte that should not appear in the command line to use as a sed
# marker when doing regular expression replacements.
local M=$'\001'
# Default config location: same name/directory as this script,
# with a .config file extension, ie ensure_secure_kernelparams.config.
local configfile="$(dirname "$0")/${0/%.sh/.config}"
# Or, maybe a config was provided on the command line.
if [[ $# -eq 2 ]]; then
configfile="$2"
fi
# Either way, load test-expectations data from config.
. "$configfile" || return 1
# Set up the image on a loopback device so it's faster to access.
local loopdev
loopdev=$(loopback_partscan "${image}")
# TODO(jimhebert): Perform the kernel security tests on both the kernel
# partitions. Here, we just run it on kernel partition 4
# which is the install kernel on the recovery image.
# crosbug.com/24274
local loop_kern="${loopdev}p4"
local loop_rootfs="${loopdev}p3"
local rootfs=$(make_temp_dir)
sudo mount -o ro "${loop_rootfs}" "${rootfs}"
# Pick the right set of test-expectation data to use.
local boardvar=$(get_boardvar_from_lsb_release "${rootfs}")
eval "required_kparams=(\"\${required_kparams_${boardvar}[@]}\")"
eval "required_kparams_regex=(\"\${required_kparams_regex_${boardvar}[@]}\")"
eval "optional_kparams=(\"\${optional_kparams_${boardvar}[@]}\")"
eval "optional_kparams_regex=(\"\${optional_kparams_regex_${boardvar}[@]}\")"
eval "required_dmparams=(\"\${required_dmparams_${boardvar}[@]}\")"
eval "required_dmparams_regex=(\"\${required_dmparams_regex_${boardvar}[@]}\")"
output+="required_kparams=(\n"
output+="$(printf "\t'%s'\n" "${required_kparams[@]}")\n)\n"
output+="required_kparams_regex=(\n"
output+="$(printf "\t'%s'\n" "${required_kparams_regex[@]}")\n)\n"
output+="optional_kparams=(\n"
output+="$(printf "\t'%s'\n" "${optional_kparams[@]}")\n)\n"
output+="optional_kparams_regex=(\n"
output+="$(printf "\t'%s'\n" "${optional_kparams_regex[@]}")\n)\n"
output+="required_dmparams=(\n"
output+="$(printf "\t'%s'\n" "${required_dmparams[@]}")\n)\n"
output+="required_dmparams_regex=(\n"
output+="$(printf "\t'%s'\n" "${required_dmparams_regex[@]}")\n)\n"
# Divide the dm params from the rest and process seperately.
local kparams=$(sudo dump_kernel_config "${loop_kern}")
local dmparams=$(get_dmparams "$kparams")
local kparams_nodm=$(kparams_remove_dm "$kparams")
output+="\nkparams='${kparams}'\n"
output+="\ndmparams='${dmparams}'\n"
output+="\nkparams_nodm='${kparams_nodm}'\n"
mangled_dmparams=$(dmparams_mangle "${dmparams}")
output+="\nmangled_dmparams='${mangled_dmparams}'\n"
# Special-case handling of the dm= param:
testfail=1
for expected_dmparams in "${required_dmparams[@]}"; do
# Filter out all dynamic parameters.
if [ "$mangled_dmparams" = "$expected_dmparams" ]; then
testfail=0
break
fi
done
local sedout
for expected_dmparams in "${required_dmparams_regex[@]}"; do
if ! sedout=$(echo "${mangled_dmparams}" | \
sed "s${M}^${expected_dmparams}\$${M}${M}"); then
echo "INTERNAL ERROR from sed script: ${expected_dmparams}"
break
elif [[ -z "${sedout}" ]]; then
testfail=0
break
fi
done
if [ $testfail -eq 1 ]; then
echo "Kernel dm= parameter does not match any expected values!"
echo "Actual value: ${dmparams}"
echo "Mangled testing value: ${mangled_dmparams}"
if [[ ${#required_dmparams[@]} -gt 0 ]]; then
echo "Expected -- only one need match:"
printf " >>> %s\n" "${required_dmparams[@]}"
fi
if [[ ${#required_dmparams_regex[@]} -gt 0 ]]; then
echo "Expected (regex) -- only one need match:"
printf " >>> %s\n" "${required_dmparams_regex[@]}"
fi
fi
# Ensure all other required params are present.
for param in "${required_kparams[@]}"; do
if [[ "$kparams_nodm" != *$param* ]]; then
echo "Kernel parameters missing required value: $param"
testfail=1
else
# Remove matched params as we go. If all goes well, kparams_nodm
# will be nothing left but whitespace by the end.
param=$(escape_regexmetas "$param")
kparams_nodm=$(echo " ${kparams_nodm} " |
sed "s${M} ${param} ${M} ${M}")
fi
done
# Ensure all other required regex params are present.
for param in "${required_kparams_regex[@]}"; do
pre_sed=" ${kparams_nodm} "
kparams_nodm=$(echo "${pre_sed}" | sed "s${M} ${param} ${M} ${M}")
if [[ "${pre_sed}" == "${kparams_nodm}" ]]; then
echo "Kernel parameters missing required value: ${param}"
testfail=1
fi
done
# Check-off each of the allowed-but-optional params that were present.
for param in "${optional_kparams[@]}"; do
param=$(escape_regexmetas "$param")
kparams_nodm=$(echo " ${kparams_nodm} " |
sed "s${M} ${param} ${M} ${M}")
done
# Check-off each of the allowed-but-optional params that were present.
for param in "${optional_kparams_regex[@]}"; do
kparams_nodm=$(echo " ${kparams_nodm} " |
sed "s${M} ${param} ${M} ${M}")
done
# This section enforces the default-deny for any unexpected params
# not already processed by one of the above loops.
if [[ ! -z ${kparams_nodm// /} ]]; then
echo "Unexpected kernel parameters found:"
echo " $(echo "${kparams_nodm}" | sed -r 's: +: :g')"
testfail=1
fi
if [[ ${testfail} -eq 1 ]]; then
echo "Debug output:"
printf '%b\n' "${output}"
echo "(actual error will be at the top of output)"
fi
exit $testfail
}
main $@