1 #!/bin/bash 2 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 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. 8 set -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. 15 get_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 23 kparams_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. 31 dmparams_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. 53 escape_regexmetas() { 54 echo "$1" | sed 's/\([^a-zA-Z0-9]\)/\\\1/g' 55 } 56 57 usage() { 58 echo "Usage $PROG image [config]" 59 } 60 61 main() { 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 71 if [[ $# -ne 1 ]] && [[ $# -ne 2 ]]; then 72 usage 73 exit 1 74 fi 75 76 local image="$1" 77 78 # A byte that should not appear in the command line to use as a sed 79 # marker when doing regular expression replacements. 80 local M=$'\001' 81 82 # Default config location: same name/directory as this script, 83 # with a .config file extension, ie ensure_secure_kernelparams.config. 84 local configfile="$(dirname "$0")/${0/%.sh/.config}" 85 # Or, maybe a config was provided on the command line. 86 if [[ $# -eq 2 ]]; then 87 configfile="$2" 88 fi 89 # Either way, load test-expectations data from config. 90 . "$configfile" || return 1 91 92 local kernelblob=$(make_temp_file) 93 # TODO(jimhebert): Perform the kernel security tests on both the kernel 94 # partitions. Here, we just run it on kernel partition 4 95 # which is the install kernel on the recovery image. 96 # crosbug.com/24274 97 extract_image_partition "$image" 4 "$kernelblob" 98 local rootfs=$(make_temp_dir) 99 mount_image_partition_ro "$image" 3 "$rootfs" 100 101 # Pick the right set of test-expectation data to use. The cuts 102 # turn e.g. x86-foo as a well as x86-foo-pvtkeys into x86_foo. 103 local board=$(grep CHROMEOS_RELEASE_BOARD= "$rootfs/etc/lsb-release" | \ 104 cut -d = -f 2 | cut -d - -f 1,2 --output-delimiter=_) 105 eval "required_kparams=(\"\${required_kparams_$board[@]}\")" 106 eval "required_kparams_regex=(\"\${required_kparams_regex_$board[@]}\")" 107 eval "optional_kparams=(\"\${optional_kparams_$board[@]}\")" 108 eval "optional_kparams_regex=(\"\${optional_kparams_regex_$board[@]}\")" 109 eval "required_dmparams=(\"\${required_dmparams_$board[@]}\")" 110 eval "required_dmparams_regex=(\"\${required_dmparams_regex_$board[@]}\")" 111 output+="required_kparams=(\n" 112 output+="$(printf "\t'%s'\n" "${required_kparams[@]}")\n)\n" 113 output+="required_kparams_regex=(\n" 114 output+="$(printf "\t'%s'\n" "${required_kparams_regex[@]}")\n)\n" 115 output+="optional_kparams=(\n" 116 output+="$(printf "\t'%s'\n" "${optional_kparams[@]}")\n)\n" 117 output+="optional_kparams_regex=(\n" 118 output+="$(printf "\t'%s'\n" "${optional_kparams_regex[@]}")\n)\n" 119 output+="required_dmparams=(\n" 120 output+="$(printf "\t'%s'\n" "${required_dmparams[@]}")\n)\n" 121 output+="required_dmparams_regex=(\n" 122 output+="$(printf "\t'%s'\n" "${required_dmparams_regex[@]}")\n)\n" 123 124 # Divide the dm params from the rest and process seperately. 125 local kparams=$(dump_kernel_config "$kernelblob") 126 local dmparams=$(get_dmparams "$kparams") 127 local kparams_nodm=$(kparams_remove_dm "$kparams") 128 129 output+="\nkparams='${kparams}'\n" 130 output+="\ndmparams='${dmparams}'\n" 131 output+="\nkparams_nodm='${kparams_nodm}'\n" 132 133 mangled_dmparams=$(dmparams_mangle "${dmparams}") 134 output+="\nmangled_dmparams='${mangled_dmparams}'\n" 135 # Special-case handling of the dm= param: 136 testfail=1 137 for expected_dmparams in "${required_dmparams[@]}"; do 138 # Filter out all dynamic parameters. 139 if [ "$mangled_dmparams" = "$expected_dmparams" ]; then 140 testfail=0 141 break 142 fi 143 done 144 145 for expected_dmparams in "${required_dmparams_regex[@]}"; do 146 if [[ -z $(echo "${mangled_dmparams}" | \ 147 sed "s${M}^${expected_dmparams}\$${M}${M}") ]]; then 148 testfail=0 149 break 150 fi 151 done 152 153 if [ $testfail -eq 1 ]; then 154 echo "Kernel dm= parameter does not match any expected values!" 155 echo "Actual: $dmparams" 156 echo "Expected: ${required_dmparams[@]}" 157 fi 158 159 # Ensure all other required params are present. 160 for param in "${required_kparams[@]}"; do 161 if [[ "$kparams_nodm" != *$param* ]]; then 162 echo "Kernel parameters missing required value: $param" 163 testfail=1 164 else 165 # Remove matched params as we go. If all goes well, kparams_nodm 166 # will be nothing left but whitespace by the end. 167 param=$(escape_regexmetas "$param") 168 kparams_nodm=$(echo " ${kparams_nodm} " | 169 sed "s${M} ${param} ${M} ${M}") 170 fi 171 done 172 173 # Ensure all other required regex params are present. 174 for param in "${required_kparams_regex[@]}"; do 175 pre_sed=" ${kparams_nodm} " 176 kparams_nodm=$(echo "${pre_sed}" | sed "s${M} ${param} ${M} ${M}") 177 if [[ "${pre_sed}" == "${kparams_nodm}" ]]; then 178 echo "Kernel parameters missing required value: ${param}" 179 testfail=1 180 fi 181 done 182 183 # Check-off each of the allowed-but-optional params that were present. 184 for param in "${optional_kparams[@]}"; do 185 param=$(escape_regexmetas "$param") 186 kparams_nodm=$(echo " ${kparams_nodm} " | 187 sed "s${M} ${param} ${M} ${M}") 188 done 189 190 # Check-off each of the allowed-but-optional params that were present. 191 for param in "${optional_kparams_regex[@]}"; do 192 kparams_nodm=$(echo " ${kparams_nodm} " | 193 sed "s${M} ${param} ${M} ${M}") 194 done 195 196 # This section enforces the default-deny for any unexpected params 197 # not already processed by one of the above loops. 198 if [[ ! -z ${kparams_nodm// /} ]]; then 199 echo "Unexpected kernel parameters found:" 200 echo " $(echo "${kparams_nodm}" | sed -r 's: +: :g')" 201 testfail=1 202 fi 203 204 if [[ ${testfail} -eq 1 ]]; then 205 echo "Debug output:" 206 printf '%b\n' "${output}" 207 echo "(actual error will be at the top of output)" 208 fi 209 210 exit $testfail 211 } 212 213 main $@ 214