1#!/bin/bash 2 3# Copyright 2011 The ChromiumOS Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7# Abort on error. 8set -e 9 10# Load common constants and variables. 11. "$(dirname "$0")/common.sh" 12 13# Given a kernel boot param string which includes ...dm="dmstuff"... 14# this returns the dmstuff by itself. 15get_dmparams() { 16 echo "$1" | sed 's/^.*\ dm="\([^"]*\)".*/\1/' 17} 18 19# Given a kernel boot param string which includes ...dm="stuff"... 20# this returns the param string with the dm="..." section removed. 21# Useful in conjunction with get_dmparams to divide and process 22# the two sections of parameters in seperate passes 23kparams_remove_dm() { 24 echo "$1" | sed 's/dm="[^"]*"//' 25} 26 27# Given a dm param string which includes dynamic values, return the 28# same string with these values replaced by a magic string placeholder. 29# This same magic placeholder is used in the config file, for comparison 30# purposes. 31dmparams_mangle() { 32 local dmparams=$1 33 # First handle new key-value style verity parameters. 34 dmparams=$(echo "$dmparams" | 35 sed -e 's/root_hexdigest=[0-9a-fA-F]*/root_hexdigest=MAGIC_HASH/' | 36 sed -e 's/salt=[0-9a-fA-F]*/salt=MAGIC_SALT'/) 37 # If we didn't substitute the MAGIC_HASH yet, these are the old 38 # verity parameter format. 39 if [[ $dmparams != *MAGIC_HASH* ]]; then 40 dmparams=$(echo $dmparams | sed 's/sha1 [0-9a-fA-F]*/sha1 MAGIC_HASH/') 41 fi 42 # If we have bootcache enabled, replace its copy of the root_hexdigest 43 # with MAGIC_HASH. The parameter is positional. 44 if [[ $dmparams == *bootcache* ]]; then 45 dmparams=$(echo $dmparams | 46 sed -r 's:(bootcache (PARTUUID=)?%U(/PARTNROFF=|\+)1 [0-9]+) [0-9a-fA-F]+:\1 MAGIC_HASH:') 47 fi 48 echo $dmparams 49} 50 51# This escapes any non-alphanum character, since many such characters 52# are regex metacharacters. 53escape_regexmetas() { 54 echo "$1" | sed 's/\([^a-zA-Z0-9]\)/\\\1/g' 55} 56 57usage() { 58 echo "Usage $PROG image [config]" 59} 60 61main() { 62 # We want to catch all the discrepancies, not just the first one. 63 # So, any time we find one, we set testfail=1 and continue. 64 # When finished we will use testfail to determine our exit value. 65 local testfail=0 66 # A buffer to include useful information that we dump when things fail. 67 local output 68 # Copy of a string before it has been through sed 69 local pre_sed 70 # Where the disk image is mounted. 71 local loopdev 72 73 if [[ $# -ne 1 ]] && [[ $# -ne 2 ]]; then 74 usage 75 exit 1 76 fi 77 78 local image="$1" 79 80 # A byte that should not appear in the command line to use as a sed 81 # marker when doing regular expression replacements. 82 local M=$'\001' 83 84 # Default config location: same name/directory as this script, 85 # with a .config file extension, ie ensure_secure_kernelparams.config. 86 local configfile="$(dirname "$0")/${0/%.sh/.config}" 87 # Or, maybe a config was provided on the command line. 88 if [[ $# -eq 2 ]]; then 89 configfile="$2" 90 fi 91 # Either way, load test-expectations data from config. 92 . "$configfile" || return 1 93 94 # Set up the image on a loopback device so it's faster to access. 95 local loopdev 96 loopdev=$(loopback_partscan "${image}") 97 98 # TODO(jimhebert): Perform the kernel security tests on both the kernel 99 # partitions. Here, we just run it on kernel partition 4 100 # which is the install kernel on the recovery image. 101 # crosbug.com/24274 102 local loop_kern="${loopdev}p4" 103 local rootfs=$(make_temp_dir) 104 mount_loop_image_partition_ro "${loopdev}" 3 "${rootfs}" 105 106 # Pick the right set of test-expectation data to use. 107 local boardvar=$(get_boardvar_from_lsb_release "${rootfs}") 108 eval "required_kparams=(\"\${required_kparams_${boardvar}[@]}\")" 109 eval "required_kparams_regex=(\"\${required_kparams_regex_${boardvar}[@]}\")" 110 eval "optional_kparams=(\"\${optional_kparams_${boardvar}[@]}\")" 111 eval "optional_kparams_regex=(\"\${optional_kparams_regex_${boardvar}[@]}\")" 112 eval "required_dmparams=(\"\${required_dmparams_${boardvar}[@]}\")" 113 eval "required_dmparams_regex=(\"\${required_dmparams_regex_${boardvar}[@]}\")" 114 output+="required_kparams=(\n" 115 output+="$(printf "\t'%s'\n" "${required_kparams[@]}")\n)\n" 116 output+="required_kparams_regex=(\n" 117 output+="$(printf "\t'%s'\n" "${required_kparams_regex[@]}")\n)\n" 118 output+="optional_kparams=(\n" 119 output+="$(printf "\t'%s'\n" "${optional_kparams[@]}")\n)\n" 120 output+="optional_kparams_regex=(\n" 121 output+="$(printf "\t'%s'\n" "${optional_kparams_regex[@]}")\n)\n" 122 output+="required_dmparams=(\n" 123 output+="$(printf "\t'%s'\n" "${required_dmparams[@]}")\n)\n" 124 output+="required_dmparams_regex=(\n" 125 output+="$(printf "\t'%s'\n" "${required_dmparams_regex[@]}")\n)\n" 126 127 # Divide the dm params from the rest and process seperately. 128 local kparams=$(sudo futility dump_kernel_config "${loop_kern}") 129 local dmparams=$(get_dmparams "$kparams") 130 local kparams_nodm=$(kparams_remove_dm "$kparams") 131 132 output+="\nkparams='${kparams}'\n" 133 output+="\ndmparams='${dmparams}'\n" 134 output+="\nkparams_nodm='${kparams_nodm}'\n" 135 136 mangled_dmparams=$(dmparams_mangle "${dmparams}") 137 output+="\nmangled_dmparams='${mangled_dmparams}'\n" 138 # Special-case handling of the dm= param: 139 testfail=1 140 for expected_dmparams in "${required_dmparams[@]}"; do 141 # Filter out all dynamic parameters. 142 if [ "$mangled_dmparams" = "$expected_dmparams" ]; then 143 testfail=0 144 break 145 fi 146 done 147 148 local sedout 149 for expected_dmparams in "${required_dmparams_regex[@]}"; do 150 if ! sedout=$(echo "${mangled_dmparams}" | \ 151 sed "s${M}^${expected_dmparams}\$${M}${M}"); then 152 echo "INTERNAL ERROR from sed script: ${expected_dmparams}" 153 break 154 elif [[ -z "${sedout}" ]]; then 155 testfail=0 156 break 157 fi 158 done 159 160 if [ $testfail -eq 1 ]; then 161 echo "Kernel dm= parameter does not match any expected values!" 162 echo "Actual value: ${dmparams}" 163 echo "Mangled testing value: ${mangled_dmparams}" 164 if [[ ${#required_dmparams[@]} -gt 0 ]]; then 165 echo "Expected -- only one need match:" 166 printf " >>> %s\n" "${required_dmparams[@]}" 167 fi 168 if [[ ${#required_dmparams_regex[@]} -gt 0 ]]; then 169 echo "Expected (regex) -- only one need match:" 170 printf " >>> %s\n" "${required_dmparams_regex[@]}" 171 fi 172 fi 173 174 # Ensure all other required params are present. 175 for param in "${required_kparams[@]}"; do 176 if [[ "$kparams_nodm" != *$param* ]]; then 177 echo "Kernel parameters missing required value: $param" 178 testfail=1 179 else 180 # Remove matched params as we go. If all goes well, kparams_nodm 181 # will be nothing left but whitespace by the end. 182 param=$(escape_regexmetas "$param") 183 kparams_nodm=$(echo " ${kparams_nodm} " | 184 sed "s${M} ${param} ${M} ${M}") 185 fi 186 done 187 188 # Ensure all other required regex params are present. 189 for param in "${required_kparams_regex[@]}"; do 190 pre_sed=" ${kparams_nodm} " 191 kparams_nodm=$(echo "${pre_sed}" | sed "s${M} ${param} ${M} ${M}") 192 if [[ "${pre_sed}" == "${kparams_nodm}" ]]; then 193 echo "Kernel parameters missing required value: ${param}" 194 testfail=1 195 fi 196 done 197 198 # Check-off each of the allowed-but-optional params that were present. 199 for param in "${optional_kparams[@]}"; do 200 param=$(escape_regexmetas "$param") 201 kparams_nodm=$(echo " ${kparams_nodm} " | 202 sed "s${M} ${param} ${M} ${M}") 203 done 204 205 # Check-off each of the allowed-but-optional params that were present. 206 for param in "${optional_kparams_regex[@]}"; do 207 kparams_nodm=$(echo " ${kparams_nodm} " | 208 sed "s${M} ${param} ${M} ${M}") 209 done 210 211 # This section enforces the default-deny for any unexpected params 212 # not already processed by one of the above loops. 213 if [[ ! -z ${kparams_nodm// /} ]]; then 214 echo "Unexpected kernel parameters found:" 215 echo " $(echo "${kparams_nodm}" | sed -r 's: +: :g')" 216 testfail=1 217 fi 218 219 if [[ ${testfail} -eq 1 ]]; then 220 echo "Debug output:" 221 printf '%b\n' "${output}" 222 echo "(actual error will be at the top of output)" 223 fi 224 225 exit $testfail 226} 227 228main $@ 229