xref: /aosp_15_r20/external/vboot_reference/scripts/image_signing/ensure_sane_lsb-release.sh (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1#!/bin/bash
2
3# Copyright 2012 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
10LSB_FILE=/etc/lsb-release
11
12# Load common constants and variables.
13. "$(dirname "$0")/common.sh"
14
15usage() {
16  echo "Usage $PROG image [config]"
17}
18
19# Usage: lsbequals path-to-lsb-file key expected-value
20# Returns 0 if they match, 1 otherwise.
21# Also outputs a warning message if they don't match.
22lsbequals() {
23  local lsbfile="$1"
24  local key="$2"
25  local expectval="$3"
26  local realval=$(lsbval "$lsbfile" $key)
27  if [ "$realval" != "$expectval" ]; then
28    error "${key} mismatch. Expected '${expectval}'," \
29      "image contains '${realval}'"
30    return 1
31  fi
32  return 0
33}
34
35# Usage: check_keyval_in_list lsbfile lsbkey [list of values]
36# Extracts the lsb-release value for the specified key, and confirms it
37# matches one of the allowed values specified in value_array.
38# Implementation note:
39# You can't really pass bash arrays to functions. Best you can do is either
40# serialize to string/pass/deserialize (e.g. using whitspace/IFS hacks), or,
41# let the array contents be received as multiple arguments to the target
42# function. We take the latter approach here, hence the shift's to get the
43# first 2 arguments out, before we process the rest of the varargs.
44check_keyval_in_list() {
45  local lsbfile="$1"
46  shift
47  local lsbkey="$1"
48  shift
49  local lsbval=$(lsbval "$lsbfile" "$lsbkey")
50  while [ $# -gt 0 ]; do
51    if [ "$lsbval" == "$1" ]; then
52      return 0
53    fi
54    shift
55  done
56  # If we get here, it wasn't found
57  error "${lsbkey}: Value '${lsbval}' was not recognized"
58  return 1
59}
60
61# Usage: lsb_syntaxcheck path-to-lsb-file
62# Enforces a number of basic validity checks on the overall format and contents
63# of the lsb-release file:
64# - Every line is "key=value".
65# - No space after key, no space before value.
66# - key is all A-Z or _, but not starting with _.
67# - value is made up of printable characters, or is empty.
68# - The whole file is a reasonable size (4kb).
69lsb_syntaxcheck() {
70  local lsbfile="$1"
71  syntaxbad=0
72  # Checks for key being A-Z_, 1 or more characters, not starting with _.
73  # Also checks for = with no spaces on either side.
74  # Checks that the value contains printables (and not starting with space).
75  # Alternatively, the value is permitted to be empty (0 chars) too.
76  # Allow comments to start with #.
77  badlines=$(grep -Ev \
78               -e '^[A-Z][A-Z_]*=([[:graph:]][[:print:]]*)?$' \
79               -e '^[[:space:]]*#' "${lsbfile}")
80  if [ -n "$badlines" ]; then
81    syntaxbad=1
82    error "${lsbfile}: Some lines seem non-well-formed:"
83    error "${badlines}"
84  fi
85
86  # Overall file size check:
87  size=$(ls -sk "$lsbfile" | cut -d ' ' -f 1)
88  if [ $size -gt 4 ]; then
89    syntaxbad=1
90    error "${lsbfile}: This file exceeds 4kb"
91  fi
92  return $syntaxbad
93}
94
95main() {
96  # We want to catch all the discrepancies, not just the first one.
97  # So, any time we find one, we set testfail=1 and continue.
98  # When finished we will use testfail to determine our exit value.
99  local testfail=0
100
101  if [ $# -ne 1 ] && [ $# -ne 2 ]; then
102    usage
103    exit 1
104  fi
105
106  local image="$1"
107
108  # Default config location: same directory as this script.
109  local configfile="$(dirname "$0")/default_lsb_release.config"
110  # Or, maybe a config was provided on the command line.
111  if [ $# -eq 2 ]; then
112    configfile="$2"
113  fi
114  # Either way, load test-expectations data from config.
115  info "Loading config from ${configfile}"
116  . "$configfile" || return 1
117
118  local loopdev rootfs
119  if [[ -d "${image}" ]]; then
120    # We're given a mounted rootfs.
121    rootfs="${image}"
122  else
123    # Mount the disk image.
124    loopdev=$(loopback_partscan "${image}")
125    rootfs=$(make_temp_dir)
126    mount_loop_image_partition_ro "${loopdev}" 3 "${rootfs}"
127  fi
128  local lsb="$rootfs/$LSB_FILE"
129
130  # Basic syntax check first.
131  lsb_syntaxcheck "$lsb" || testfail=1
132
133  lsbequals $lsb CHROMEOS_AUSERVER "$expected_auserver" || testfail=1
134  if [[ "${#expected_release_names[@]}" -eq 0 ]]; then
135    expected_release_names=( "${expected_release_name}" )
136  fi
137  check_keyval_in_list "${lsb}" CHROMEOS_RELEASE_NAME \
138    "${expected_release_names[@]}" || testfail=1
139  check_keyval_in_list $lsb CHROMEOS_RELEASE_TRACK \
140    "${expected_release_tracks[@]}" || testfail=1
141
142  local board=$(get_board_from_lsb_release "${rootfs}")
143  if check_keyval_in_list $lsb CHROMEOS_RELEASE_BOARD \
144    "${expected_boards[@]}"; then
145    local boardvar=$(get_boardvar_from_lsb_release "${rootfs}")
146    channel=$(lsbval $lsb CHROMEOS_RELEASE_TRACK)
147    # For a canary or dogfood channel, appid maybe a different default value.
148    if [[ "${channel}" == 'canary-channel' ||
149          "${channel}" == 'dogfood-channel' ]]; then
150      eval "expected_appid=\"\$expected_appid_${channel%\-channel}\""
151    else
152      eval "expected_appid=\"\$expected_appid_$boardvar\""
153    fi
154    lsbequals $lsb CHROMEOS_RELEASE_APPID "$expected_appid" || testfail=1
155  else # unrecognized board
156    testfail=1
157    error "Unknown board: ${board}"
158  fi
159
160  exit $testfail
161}
162
163main "$@"
164