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.
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
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
213main $@
214