Home | History | Annotate | Download | only in image_signing
      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